LIBRERIAS

WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Installing package into 㤼㸱C:/Users/igalk/OneDrive/Documentos/R/win-library/4.0㤼㸲
(as 㤼㸱lib㤼㸲 is unspecified)
also installing the dependencies 㤼㸱bitops㤼㸲, 㤼㸱coda㤼㸲, 㤼㸱gtools㤼㸲, 㤼㸱caTools㤼㸲, 㤼㸱statnet.common㤼㸲, 㤼㸱TSP㤼㸲, 㤼㸱qap㤼㸲, 㤼㸱gclus㤼㸲, 㤼㸱gplots㤼㸲, 㤼㸱registry㤼㸲, 㤼㸱network㤼㸲, 㤼㸱sna㤼㸲, 㤼㸱shape㤼㸲, 㤼㸱seriation㤼㸲, 㤼㸱igraph㤼㸲, 㤼㸱ggnetwork㤼㸲, 㤼㸱plotly㤼㸲, 㤼㸱visNetwork㤼㸲, 㤼㸱discretization㤼㸲, 㤼㸱glmnet㤼㸲, 㤼㸱pmml㤼㸲, 㤼㸱XML㤼㸲, 㤼㸱arulesViz㤼㸲, 㤼㸱arulesCBA㤼㸲

  There are binary versions available but the source versions are later:

  Binaries will be installed
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/bitops_1.0-7.zip'
Content type 'application/zip' length 42425 bytes (41 KB)
downloaded 41 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/coda_0.19-4.zip'
Content type 'application/zip' length 322755 bytes (315 KB)
downloaded 315 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/gtools_3.9.2.zip'
Content type 'application/zip' length 366894 bytes (358 KB)
downloaded 358 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/caTools_1.18.2.zip'
Content type 'application/zip' length 316400 bytes (308 KB)
downloaded 308 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/statnet.common_4.5.0.zip'
Content type 'application/zip' length 246982 bytes (241 KB)
downloaded 241 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/TSP_1.1-10.zip'
Content type 'application/zip' length 1026352 bytes (1002 KB)
downloaded 1002 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/qap_0.1-1.zip'
Content type 'application/zip' length 537689 bytes (525 KB)
downloaded 525 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/gclus_1.3.2.zip'
Content type 'application/zip' length 416500 bytes (406 KB)
downloaded 406 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/gplots_3.1.1.zip'
Content type 'application/zip' length 603126 bytes (588 KB)
downloaded 588 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/registry_0.5-1.zip'
Content type 'application/zip' length 197181 bytes (192 KB)
downloaded 192 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/network_1.17.1.zip'
Content type 'application/zip' length 852583 bytes (832 KB)
downloaded 832 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/sna_2.6.zip'
Content type 'application/zip' length 1316382 bytes (1.3 MB)
downloaded 1.3 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/shape_1.4.6.zip'
Content type 'application/zip' length 788976 bytes (770 KB)
downloaded 770 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/seriation_1.2-9.zip'
Content type 'application/zip' length 1232636 bytes (1.2 MB)
downloaded 1.2 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/igraph_1.2.6.zip'
Content type 'application/zip' length 9346426 bytes (8.9 MB)
downloaded 8.9 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/plotly_4.9.4.1.zip'
Content type 'application/zip' length 3031022 bytes (2.9 MB)
downloaded 2.9 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/visNetwork_2.0.9.zip'
Content type 'application/zip' length 4595190 bytes (4.4 MB)
downloaded 4.4 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/discretization_1.0-1.zip'
Content type 'application/zip' length 100041 bytes (97 KB)
downloaded 97 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/glmnet_4.1-1.zip'
Content type 'application/zip' length 2255757 bytes (2.2 MB)
downloaded 2.2 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/pmml_2.4.0.zip'
Content type 'application/zip' length 664041 bytes (648 KB)
downloaded 648 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/XML_3.99-0.6.zip'
Content type 'application/zip' length 4260833 bytes (4.1 MB)
downloaded 4.1 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/arulesViz_1.5-0.zip'
Content type 'application/zip' length 1758920 bytes (1.7 MB)
downloaded 1.7 MB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/arulesCBA_1.2.0.zip'
Content type 'application/zip' length 225149 bytes (219 KB)
downloaded 219 KB

trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.0/arules_1.6-8.zip'
Content type 'application/zip' length 2575551 bytes (2.5 MB)
downloaded 2.5 MB
package ‘bitops’ successfully unpacked and MD5 sums checked
package ‘coda’ successfully unpacked and MD5 sums checked
package ‘gtools’ successfully unpacked and MD5 sums checked
package ‘caTools’ successfully unpacked and MD5 sums checked
package ‘statnet.common’ successfully unpacked and MD5 sums checked
package ‘TSP’ successfully unpacked and MD5 sums checked
package ‘qap’ successfully unpacked and MD5 sums checked
package ‘gclus’ successfully unpacked and MD5 sums checked
package ‘gplots’ successfully unpacked and MD5 sums checked
package ‘registry’ successfully unpacked and MD5 sums checked
package ‘network’ successfully unpacked and MD5 sums checked
package ‘sna’ successfully unpacked and MD5 sums checked
package ‘shape’ successfully unpacked and MD5 sums checked
package ‘seriation’ successfully unpacked and MD5 sums checked
package ‘igraph’ successfully unpacked and MD5 sums checked
package ‘plotly’ successfully unpacked and MD5 sums checked
package ‘visNetwork’ successfully unpacked and MD5 sums checked
package ‘discretization’ successfully unpacked and MD5 sums checked
package ‘glmnet’ successfully unpacked and MD5 sums checked
package ‘pmml’ successfully unpacked and MD5 sums checked
package ‘XML’ successfully unpacked and MD5 sums checked
package ‘arulesViz’ successfully unpacked and MD5 sums checked
package ‘arulesCBA’ successfully unpacked and MD5 sums checked
package ‘arules’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\igalk\AppData\Local\Temp\RtmpMjsq8N\downloaded_packages
installing the source package 㤼㸱ggnetwork㤼㸲

trying URL 'https://cran.rstudio.com/src/contrib/ggnetwork_0.5.9.tar.gz'
Content type 'application/x-gzip' length 2092646 bytes (2.0 MB)
downloaded 2.0 MB
* installing *source* package 'ggnetwork' ...
** package 'ggnetwork' successfully unpacked and MD5 sums checked
** using staged installation
** R
** inst
** byte-compile and prepare package for lazy loading
Warning message:
package 'ggplot2' was built under R version 4.0.5 
** help
*** installing help indices
  converting help for package 'ggnetwork'
    finding HTML links ... done
    format_fortify                          html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/format_fortify.Rd:69: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/format_fortify.Rd:24: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
    fortify                                 html  
    fortify.igraph                          html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/fortify.igraph.Rd:5: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/fortify.igraph.Rd:56: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/fortify.igraph.Rd:19: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/fortify.igraph.Rd:24: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
    fortify.network                         html  
    geom_edges                              html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/geom_edges.Rd:84: file link 'geom_curve' in package 'ggplot2' does not exist and so has been treated as a topic
    geom_edgetext                           html  
    finding level-2 HTML links ... done

Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/geom_edgetext.Rd:104: file link 'geom_label' in package 'ggplot2' does not exist and so has been treated as a topic
    geom_edgetext_repel                     html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/geom_edgetext_repel.Rd:121: file link 'geom_label_repel' in package 'ggrepel' does not exist and so has been treated as a topic
    geom_nodes                              html  
    geom_nodetext                           html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/geom_nodetext.Rd:106: file link 'geom_label' in package 'ggplot2' does not exist and so has been treated as a topic
    geom_nodetext_repel                     html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/geom_nodetext_repel.Rd:118: file link 'geom_label_repel' in package 'ggrepel' does not exist and so has been treated as a topic
    ggnetwork                               html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/ggnetwork.Rd:11: file link 'igraph-package' in package 'igraph' does not exist and so has been treated as a topic
    scale_safely                            html  
    theme_blank                             html  
    theme_facet                             html  
    unit                                    html  
Rd warning: C:/Users/igalk/AppData/Local/Temp/Rtmp4eUPfq/R.INSTALL6770bf94d2a/ggnetwork/man/unit.Rd:7: file link 'unit' in package 'ggplot2' does not exist and so has been treated as a topic
** building package indices
** installing vignettes
** testing if installed package can be loaded from temporary location
*** arch - i386
Warning: package 'ggplot2' was built under R version 4.0.5
*** arch - x64
Warning: package 'ggplot2' was built under R version 4.0.5
** testing if installed package can be loaded from final location
*** arch - i386
Warning: package 'ggplot2' was built under R version 4.0.5
*** arch - x64
Warning: package 'ggplot2' was built under R version 4.0.5
** testing if installed package keeps a record of temporary installation path
* DONE (ggnetwork)

The downloaded source packages are in
    ‘C:\Users\igalk\AppData\Local\Temp\RtmpMjsq8N\downloaded_packages’
Loading required package: Matrix

Attaching package: 㤼㸱Matrix㤼㸲

The following objects are masked from 㤼㸱package:tidyr㤼㸲:

    expand, pack, unpack


Attaching package: 㤼㸱arules㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    recode

The following objects are masked from 㤼㸱package:base㤼㸲:

    abbreviate, write

CARGA DE BASES

df_lyrics
Error: object 'df_lyrics' not found

Nueva Corrección duplicados

df_audio_features <- df_audio_features_raw %>% 
  group_by(track_name, external_urls_spotify) %>% 
  mutate(artist_all = paste(artist_name, collapse = ",|,")) %>%
  ungroup() %>% 
  mutate(artist_key = sub(",|,.*", "", artist_all)) %>% 
  dplyr::select(artist_name, artist_all, artist_key, everything(.)) %>% 
  distinct(artist_key, external_urls_spotify, .keep_all = T) %>% 
  as.data.frame()
Error in df_audio_features_raw %>% group_by(track_name, external_urls_spotify) %>%  : 
  could not find function "%>%"

CREACION cant_markets

Charts

df_charts <- df_charts_raw %>% 
  group_by(Artist, Track_Name, URL) %>%
  dplyr:: summarise(semanas_sum = n(),
            streams_sum = (sum(Streams, na.rm = T)/10^6 ),
            streams_min = (min(Streams)/10^6 ),
            streams_max = (max(Streams)/10^6 ),
            position_avg = mean(Position, na.rm = T),
            position_min = min(Position), 
            position_max = max(Position)) %>% 
  ungroup() %>% 
  mutate(indicador = as.numeric(streams_sum*semanas_sum/position_avg) )
`summarise()` has grouped output by 'Artist', 'Track_Name'. You can override using the `.groups` argument.

VECTORES DE FEATURES

RIGTH JOIN audio_features Y charts

#Armamos un join para tener una tabla de charts con las caracteristicas de las canciones
# deberian quedar 22993 filas completas
join_audio_charts <- df_audio_features %>% 
  select("artist_name","artist_all","artist_key",
         "track_name", "external_urls_spotify", "album_name", "album_release_year",
         all_of(features_continuas), all_of(features_categoricas)) %>% 
  right_join( df_charts,# %>%
               by = c(
                 "track_name" = "Track_Name", 
                      "artist_key" ="Artist", 
                      "external_urls_spotify" = "URL"))

#HAY CHARTS QUE NO TIENEN FEATURES. HAY QUE TENERLO EN CUENTA PARA EL ANÁLISIS
library(mice)
md.pattern(join_audio_charts, rotate.names = TRUE)
popularidad[is.na(popularidad$indicador),]

#Agregación de todas las semanas en charts

features_continuas <- c('acousticness', 'danceability', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness',   'tempo', 'valence', 'cant_markets')

features_categoricas <- c('explicit', 'key_name', 'mode_name', "key_mode", "album_type")

groupping_cols <- c("artist_name","artist_all","artist_key","track_name","external_urls_spotify","album_name","album_release_year")

numeric_col_charts <- c("Position","Streams")

week_start <- c("week_start")

chart_group <- join_audio_charts %>% 
                group_by(artist_name,artist_all,artist_key,track_name,external_urls_spotify,album_name,album_release_year)


continuas_summarized = chart_group %>% summarise_at(features_continuas, mean, na.rm = TRUE)
categoricas_summarizes = chart_group %>% summarise_at(features_categoricas, first)
numeric_charts_summarizes = chart_group %>% summarise(across(numeric_col_charts, list(min=min,max=max,avg=mean)))
Note: Using an external vector in selections is ambiguous.
i Use `all_of(numeric_col_charts)` instead of `numeric_col_charts` to silence this message.
i See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
Error: Problem with `summarise()` input `..1`.
x Can't subset columns that don't exist.
x Columns `Position` and `Streams` don't exist.
i Input `..1` is `(function (.cols = everything(), .fns = NULL, ..., .names = NULL) ...`.
i The error occurred in group 1: artist_name = "*NSYNC", artist_all = "*NSYNC", artist_key = "*NSYNC", track_name = "Merry Christmas, Happy Holidays", external_urls_spotify = "https://open.spotify.com/track/15coTBAzEN1bOeipoNDZAR", album_name = "Home For Christmas", album_release_year = 1998.
Run `rlang::last_error()` to see where the error occurred.

contar malas palabras

inspect(head(sort(rules.sub, by = "lift", decreasing = TRUE),10))
     lhs                             rhs                             support confidence  coverage     lift count
[1]  {instrumentalness=0.00e+00,                                                                                
      album_type=album,                                                                                         
      nivel_puteada=exp_alto}     => {nivel_ranking=chart_100a200} 0.1285047  0.7432432 0.1728972 1.178178    55
[2]  {instrumentalness=0.00e+00,                                                                                
      explicit=TRUE,                                                                                            
      album_type=album,                                                                                         
      nivel_puteada=exp_alto}     => {nivel_ranking=chart_100a200} 0.1285047  0.7432432 0.1728972 1.178178    55
[3]  {instrumentalness=0.00e+00,                                                                                
      album_type=album,                                                                                         
      nivel_puteada=exp_alto,                                                                                   
      nivel_popularidad=pop_bajo} => {nivel_ranking=chart_100a200} 0.1285047  0.7432432 0.1728972 1.178178    55
[4]  {instrumentalness=0.00e+00,                                                                                
      explicit=TRUE,                                                                                            
      album_type=album,                                                                                         
      nivel_puteada=exp_alto,                                                                                   
      nivel_popularidad=pop_bajo} => {nivel_ranking=chart_100a200} 0.1285047  0.7432432 0.1728972 1.178178    55
[5]  {album_type=album,                                                                                         
      nivel_puteada=exp_alto}     => {nivel_ranking=chart_100a200} 0.1728972  0.7254902 0.2383178 1.150036    74
[6]  {explicit=TRUE,                                                                                            
      album_type=album,                                                                                         
      nivel_puteada=exp_alto}     => {nivel_ranking=chart_100a200} 0.1728972  0.7254902 0.2383178 1.150036    74
[7]  {album_type=album,                                                                                         
      nivel_puteada=exp_alto,                                                                                   
      nivel_popularidad=pop_bajo} => {nivel_ranking=chart_100a200} 0.1728972  0.7254902 0.2383178 1.150036    74
[8]  {explicit=TRUE,                                                                                            
      album_type=album,                                                                                         
      nivel_puteada=exp_alto,                                                                                   
      nivel_popularidad=pop_bajo} => {nivel_ranking=chart_100a200} 0.1728972  0.7254902 0.2383178 1.150036    74
[9]  {nivel_puteada=exp_bajo}     => {nivel_ranking=chart_1a100}   0.1658879  0.4226190 0.3925234 1.144816    71
[10] {explicit=TRUE,                                                                                            
      nivel_puteada=exp_bajo}     => {nivel_ranking=chart_1a100}   0.1658879  0.4226190 0.3925234 1.144816    71

HISTOGRAMAS Y BARPLOTS DE VARIABLES


##histograma de las variables continuas de audio_features

for (i in features_continuas){

  hist(df_audio_features[,i], main = paste("Histograma de", i, "(all data)"), xlab = i)
  abline(v = mean(df_audio_features[,i], na.rm = TRUE) , col="red")
  abline(v = median(df_audio_features[,i], na.rm = TRUE) , col="blue")
  legend("topright", legend = c("Media", "Mediana"), col=c("red", "blue"), lty =1)

}

#divido los features por su distribución
features_continuas_media <- c('danceability', 'tempo', 'valence')

features_continuas_mediana <- c('acousticness', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets')


##histograma de las variables continuas de charts
for (i in c(features_continuas, "Streams")){

  hist(join_audio_charts[,i], main = paste("Histograma de", i,  "(charts)"), xlab = i)
  abline(v = mean(join_audio_charts[,i], na.rm = TRUE) , col="red")
  abline(v = median(join_audio_charts[,i], na.rm = TRUE) , col="blue")

}

#divido features de charts según su distribución
audio_charts_continuas_media <- c('duration_ms', 'valence')

audio_charts_continuas_mediana <- c('danceability', 'acousticness', 'tempo', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets', "Streams")


##medidas resumen y barplots de las variables categoricas audio_features
for(i in features_categoricas){

  barplot(sort(table(df_audio_features[,i]),decreasing = T), las=2, 
          main = paste("Barplot de", i, "(all data)"))
  # pie(table(df_features_categoricos[,i]))
}



##medidas resumen y barplots de las variables categoricas join_audio_charts

for(i in features_categoricas){
  
  barplot(sort(table(join_audio_charts[,i]),decreasing = T), las=2, 
          main = paste("Barplot de", i, "(charts)"))
  # pie(table(df_features_categoricos[,i]))
}

Analisis de la variable markets_concat

#Hago un join al revés 

df_chart_tojoin <- df_charts[,c("Track_Name", "Artist", "URL")]
df_chart_tojoin$isinchart <- 1
df_audio_features_tojoin <- df_audio_features[, c("track_name","artist_key","external_urls_spotify","markets_concat")]

join_barplot <- df_audio_features_tojoin %>% 
  select("track_name","artist_key","external_urls_spotify","markets_concat") %>% 
  left_join( df_chart_tojoin %>%
               select("Track_Name", "Artist", "URL","isinchart"),
               by = c(
                 "track_name" = "Track_Name", 
                      "artist_key" ="Artist", 
                      "external_urls_spotify" = "URL"))


join_barplot$isinchart[is.na(join_barplot$isinchart)] <- 0

join_barplot$isinchart <- factor(join_barplot$isinchart)

tabla_isinchart <- table(unlist(lapply(join_barplot[join_barplot$isinchart==1,"markets_concat"], function(x) strsplit(as.character(x), ','))))

tabla_notinchart <- table(unlist(lapply(join_barplot[join_barplot$isinchart==0,"markets_concat"], function(x) strsplit(as.character(x), ','))))

all_countries <- names(tabla_isinchart)

xlabs <- paste(paste(head(all_countries,3), collapse = ","),"...",paste(tail(all_countries,3),collapse = ","),"(ISO-Codes de Paises)",  collapse = ",")

options(scipen=999)
par(mfrow = c(1,2), las=1, mar=c(3,3,5,3), oma=c(0,1,1,1))
barplot(sort(tabla_isinchart, decreasing = TRUE), names.arg="", main ='En Charts',col=rgb(0.2,0.4,0.6,0.6),xlab = "Paises (ISO-Codes)")
# mtext(side = 1, text = xlabs, line = 1)
barplot(sort(tabla_notinchart, decreasing = TRUE), names.arg = "", main='Fuera de Charts',col=rgb(0.2,0.4,0.6,0.6), xlab = "Paises (ISO-Codes)")
mtext(side = 1, text = xlabs, line = 1, adj = 2)
mtext("Frecuencia de mercados habilitados", side = 3, line = -1, outer = TRUE, cex = 1.3, font =2 )
# mtext("Paises (ISO-Codes)", side = 3, line = -25, outer = TRUE)

CORRELACIONES

#correlaciones en audio features
x <- cor(df_audio_features[,c(features_continuas_media, features_continuas_mediana)],  use =  "complete.obs")
corrplot(x, type = "upper", title = "Correlacion de atributos de audio_features", mar=c(0,0,1,0), method="number" ,number.cex=0.7)

#correlaciones en charts
x <- cor(scale(join_audio_charts[,c(audio_charts_continuas_media, audio_charts_continuas_mediana)]), use =  "complete.obs")
corrplot(x, type = "upper", title = "Correlacion de atributos de los Charts", mar=c(0,0,1,0), method="number", number.cex=0.7 )

#chi2 test #con n grande no se puede usar este test
tabla_key_album <- table(df_audio_features$key_name, df_audio_features$album_type)
cat("Tabla de contigencia entre key y album type\n")
tabla_key_album
chisq.test(tabla_key_album)

SESGO DE VARIABLES

Boxplots Variables Numéricas sin filtrar outliers

#divido los features por su distribución
features_continuas_media <- c('danceability', 'tempo', 'valence')

features_continuas_mediana <- c('acousticness', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets')

all_features <- c(features_continuas_media, features_continuas_mediana)

par(mfrow=c(4,3))
for (feature in all_features){
  boxplot(df_audio_features[,feature], las=2, horizontal=T, main=feature)
}

Con excepción de valence el resto de las features poseían cierto sesgo. Se decidió transformar las variables que mayor sesgo poseían: duration_ms, instrumentalness, liveness, speechiness como método de corregir la distribución y achicar la cantidad de outliers. La variable loudness_reg_imp no fue modificada debido a que al ser negativa

# "danceability,tempo,valence,acousticness,duration_ms,energy,instrumentalness,liveness,speechiness,cant_markets"

#sesgos d las variables                                                   
sort(apply(df_audio_features[,features_continuas], MARGIN = 2, function(x){ (3* (mean(x,na.rm = T)-median(x, na.rm = T)))/sd(x, na.rm = T)} ))

variables_sesgo <- unlist(strsplit("acousticness,duration_ms,instrumentalness,liveness,speechiness,cant_markets,energy", ","))

df_sesgadas <- df_audio_features[,variables_sesgo]

logaritmo_ajustado = function(x,delta){
  if (x==0.0){
    return(log(0.00+delta, base = 10))
  }else{
    return(log(x, base = 10))
  }
}

delta <- 10^(-6)

df_sesgadas_log_adjust <- data.frame(apply(df_audio_features[,variables_sesgo], MARGIN = c(1,2), 
                                           function(x) logaritmo_ajustado(x,delta)))
# names(df_sesgadas_log_adjust) <- paste(names(df_sesgadas), "_log", sep="")
names(df_sesgadas_log_adjust) <- names(df_sesgadas)

df_datos <- cbind(df_sesgadas, df_sesgadas_log_adjust)



a <- df_sesgadas
b <- df_sesgadas_log_adjust
names(b) <- paste(names(df_sesgadas), "_log", sep="")
merged <- cbind(a,b)

merged <- merged[, order(names(merged))]

round(sort(apply(merged, MARGIN = 2, function(x){ (3* (mean(x,na.rm = T)-median(x, na.rm = T)))/sd(x, na.rm = T)})),2)

variables_plot <- unlist(strsplit("duration_ms", ","))
variables_plot <- append(variables_plot,paste(variables_plot,"_log", sep=""))
variables_plot <- variables_plot[order(variables_plot)]
plotear <- merged[,variables_plot]

par(mfrow = c(1,2))
for (col in names(plotear)){
  hist(plotear[,col], breaks="FD", main=col, xlab="")
}
summary(df_audio_features[,all_features])


hist(log(df_audio_features$duration_ms))

transformacion <- c('instrumentalness','loudness','liveness','speechiness', 'duration_ms')

logaritmo_ajustado = function(x,delta){
  if (x<=0.0){
    return(log(0.00+delta, base = 10))
  }else{
    return(log(x, base = 10))
  }
}

delta <- 10^(-6)

par(mfrow=c(2,4))
for (feature in transformacion){
  hist(df_audio_features[,feature], main=feature)
}
for (feature in transformacion){
  hist(unlist(lapply(df_audio_features[,feature], function(x) logaritmo_ajustado(x,delta))), main=paste(feature,"log", sep="_"))
}

inv_sqrt_ajustada = function(x, delta){
  if (x==0.0){
    return(1/sqrt(x+delta))
  }else{
    return(1/sqrt(x))
  }
}


delta <- 10^(-6)

par(mfrow=c(2,4))
for (feature in transformacion){
  hist(df_audio_features[,feature], main=feature)
}
for (feature in transformacion){
  hist(unlist(lapply(df_audio_features[,feature], function(x) inv_sqrt_ajustada(x,delta))), main=paste(feature,"inv_sqt", sep="_"))
}


par(mfrow=c(2,4))
for (feature in transformacion){
  hist(df_audio_features[,feature], main=feature)
}
for (feature in transformacion){
  hist(sqrt(df_audio_features[,feature]), main=paste(feature,"sqrt", sep="_"))
}

par(mfrow = c(2,1)) 
hist(df_audio_features[,'loudness_reg_imp'], main='loudness', xlab="")
#hist(sqrt(df_audio_features[,'loudness_reg_imp']), main= 'loudness_sqrt', xlab="")
boxplot(df_audio_features[,'loudness_reg_imp'], horizontal = T)
#boxplot(sqrt(df_audio_features[,'loudness_reg_imp']), horizontal = T)
fit <- lm(loudness~energy+acousticness, data=df_audio_features)

modelo <- fit$coefficients

df_audio_features$loudness_reg_imp <- df_audio_features$loudness

X <- df_audio_features[df_audio_features$loudness>0, c('energy', "acousticness")]

df_audio_features$loudness_reg_imp[df_audio_features$loudness>0] <- modelo[1]+modelo[2]*X[,1]+modelo[3]*X[,2]

summary(df_audio_features[,c("loudness", "loudness_reg_imp")])

summary(fit)

instrumentalness tiene mucho sesgo la variable. Se va a recurrir a una logaritmización de la variable, previa transformación del dominio, haciendo que los valores que son 0, sean en realidad 0.0000001

logaritmo_ajustado = function(x,delta){
  if (x==0.0){
    return(log(x+delta, base = 10))
  }else{
    return(log(x, base = 10))
  }
}

delta <- 10^(-6)

df_audio_features$instrumentalness_logadjust <- unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta)))

par(mfrow =c(2,2))
hist(df_audio_features$instrumentalness, main="insrumentalness", xlab="")
hist(unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta))), main='instrumentalness_logadjust', ylim = c(0,130500), xlab = "")
boxplot(df_audio_features$instrumentalness, main="", horizontal = T)
boxplot(unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta))), main="", horizontal=T)
# hist(log(1/sqrt(df_audio_features$instrumentalness+0.00001)),main='log(sqrt(x+))', ylim=c(0,130500), xlab = "")

¿Es útil esta transformación?


delta <- 10^(-6)

df_audio_features$instrumentalness_logadjust <- unlist(lapply(df_audio_features$instrumentalness, function(x) logaritmo_ajustado(x,delta)))

df_chart_tojoin <- df_charts[,c("Track_Name", "Artist", "URL")]
df_chart_tojoin$isinchart <- 1
df_audio_features_tojoin <- df_audio_features[, c("track_name","artist_key","external_urls_spotify","instrumentalness", "instrumentalness_logadjust")]

join_histogram <- df_audio_features_tojoin %>% 
  dplyr::select("track_name","artist_key","external_urls_spotify","instrumentalness", "instrumentalness_logadjust") %>% 
  left_join( df_chart_tojoin %>%
               select("Track_Name", "Artist", "URL","isinchart"),
               by = c(
                 "track_name" = "Track_Name", 
                      "artist_key" ="Artist", 
                      "external_urls_spotify" = "URL"))


join_histogram$isinchart[is.na(join_histogram$isinchart)] <- 0

join_histogram$isinchart <- factor(join_histogram$isinchart)


h11 <- hist(join_histogram[join_histogram$isinchart==1,'instrumentalness'])
h11$density <-  h11$counts/sum(h11$counts)*100

h12 <- hist(join_histogram[join_histogram$isinchart==0,'instrumentalness'])
h12$density <-  h12$counts/sum(h12$counts)*100

h21 <- hist(join_histogram[join_histogram$isinchart==1,'instrumentalness_logadjust'])
h21$density <-  h21$counts/sum(h21$counts)*100

h22 <- hist(join_histogram[join_histogram$isinchart==0,'instrumentalness_logadjust'])
h22$density <-  h22$counts/sum(h22$counts)*100

#png("C:/Users/Asus/Desktop/DATA SCIENCE/MAESTRIA/Data Mining/TP/graficos/instrumentalness.png",
#    width = 800, height = 800)
par(mfrow = c(3,2))
plot(h11, main='instrumentalness \nchart', xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
plot(h12, main='instrumentalness \nfuera chart', xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
plot(h21, main ="instrumentalness_log \nchart", xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
plot(h22, main ="instrumentalness_log \nfuera chart", xlab="", ylab="Porcentage", freq=FALSE, col='grey', ylim = c(0,100))
boxplot(join_histogram[join_histogram$isinchart==1,'instrumentalness_logadjust'], main="instrumentalness_log chart", horizontal = T)
boxplot(join_histogram[join_histogram$isinchart==0,'instrumentalness_logadjust'], main="instrumentalness_log fuera chart", horizontal = T)
#dev.off()

Z-Score de Variables que “tienden a la normal”


################################

## FILTRAMOS OUTLIERS POR Z-SCORE para 'danceability', 'tempo', 'valence'

##############################

#z-score para variables que tienden a la normal
#filtro features numericos 

#divido los features por su distribución
features_continuas_media <- c('danceability', 'tempo', 'valence')
df_audio_features_zscore_media <- df_audio_features[,features_continuas_media]

#normalizo z score con las variables que tienden a la normal

zscore_cols <- c()
for(col in names(df_audio_features_zscore_media)){
  name_col <- paste('zscore_',col, sep = "")
  zscore_cols <- append(zscore_cols, name_col)
  media <-  mean(df_audio_features_zscore_media[,col])
  stdv <- sd(df_audio_features_zscore_media[,col])
  df_audio_features_zscore_media[,name_col] <- (df_audio_features_zscore_media[,col] - media)/stdv
  }

par(mfrow=c(1,length(zscore_cols)))
lapply(zscore_cols, function(col) boxplot(df_audio_features_zscore_media[,col],xlab=col))

Analisis de Z-Score por variable

Danceability

#variable: danceability

umbral_zscore <- 3
conditions <- (df_audio_features_zscore_media$zscore_danceability> umbral_zscore) | (df_audio_features_zscore_media$zscore_danceability< -1*umbral_zscore)
df_audio_features[conditions,] %>%
  select(album_name,artist_name, danceability ) %>%
  arrange(-danceability)

Tempo

#variable: Tempo

umbral_zscore <- 3
conditions <- (df_audio_features_zscore_media$zscore_tempo> umbral_zscore) | (df_audio_features_zscore_media$zscore_tempo< -1*umbral_zscore)
df_audio_features[conditions,] %>%
  select(album_name,artist_name, tempo ) %>%
  arrange(-tempo)

Valence

#variable: valence
umbral_zscore <- 3
conditions <- (df_audio_features_zscore_media$zscore_valence> umbral_zscore) | (df_audio_features_zscore_media$zscore_valence< -1*umbral_zscore)
df_audio_features[conditions,] %>%
  select(album_name,artist_name, valence ) %>%
  arrange(-valence)

Z-Score Modificado de Variables Asimetricas

################################

## FILTRAMOS OUTLIERS POR Z-SCORE MODIFICADO para 'acousticness', 'duration_ms', 'energy',  'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets'

##############################

features_continuas_mediana <- c('acousticness', 'duration_ms', 'energy', 'instrumentalness', 'liveness', 'loudness', 'speechiness', 'cant_markets')

df_audio_features_zscore_mediana <- df_audio_features[,features_continuas_mediana]



zscoremodif_cols <- c()
for(col in names(df_audio_features_zscore_mediana)){
  name_col <- paste('zscoremodif_',col, sep = "")
  zscoremodif_cols <- append(zscoremodif_cols, name_col)
  med = median(df_audio_features_zscore_mediana[,col], na.rm = T)
  MAD = median(abs(df_audio_features_zscore_mediana[,col] - med), na.rm = T)
  df_audio_features_zscore_mediana[, name_col] <- 0.6745 * (df_audio_features_zscore_mediana[,col] - med) / MAD
}


par(mfrow=c(4,2))
lapply(zscoremodif_cols, function(col) boxplot(df_audio_features_zscore_mediana[,col],xlab=col, horizontal = T))

Revisión Variable Instrumentalness

instrumentalness <- c("instrumentalness", "zscoremodif_instrumentalness") 

x <- df_audio_features$instrumentalness

n_interv <- 10


intervalos <- round(seq(0,max(x),by=(max(x)-min(x))/n_interv),2)

labs <- c()
for (i in 1:n_interv){
lab <- paste(intervalos[i],intervalos[i+1], sep='\n')
labs <- append(labs, lab)
    
}

bins <- cut(x, n_interv, include.lowest = TRUE, labels = labs)

barplot(table(bins))

Hacemos K-means para poder discretizar la variable.

sse <- c()
for (k in 2:6){
  clusters <- kmeans(df_audio_features$instrumentalness,centers = k, iter.max = 10, nstart = k)
  sse <- append(sse, clusters$tot.withinss)
}

plot(2:6,sse, type = 'l', xlab='Cantidad de Clusters', ylab='Suma Error Cuadrático')

#k=3 
clusters3 <- kmeans(df_audio_features$instrumentalness,centers = 3, iter.max = 10, nstart = 3)

df_audio_features$clusters <- factor(clusters3$cluster)

lev <- levels(df_audio_features$clusters)

labs <- c()
for (i in lev){
  min <- min(df_audio_features$instrumentalness[df_audio_features$clusters==i])
  max <- max(df_audio_features$instrumentalness[df_audio_features$clusters==i])
  lab <- paste(min,max, sep=' - ')
  labs <- append(labs, lab)
}

labs

# barplot(table(factor(clusters3$cluster)), labels = labs)

Preguntas de investigacion

Patron Comun Canciones del Chart

¿Qué características tienen las canciones que están en el chart? ¿Cual es el patrón comun que tienen las canciones más escuchadas? (ver dispersiones, media, grafico comparativo)



#funcion para escalar variable
scale_vble <- function(x){
  (x - mean(x, na.rm = T))/sd(x, na.rm = T)
}
#anti_join
anti_join_audio_charts <- df_audio_features %>% 
  select("artist_name","artist_all", "artist_key",
         "track_name", "external_urls_spotify", "album_name", "album_release_year",
         all_of(features_continuas), all_of(features_categoricas)) %>% 
  anti_join( df_charts %>%
               select( "Track_Name", "Artist", "URL"),
               by = c("external_urls_spotify" ="URL",
                      "artist_key" ="Artist"  ))
               # by = c("track_name" = "Track_Name"))


anti_join_audio_charts_complete <- na.omit(anti_join_audio_charts)
anti_join_audio_charts_complete_scale <- anti_join_audio_charts_complete %>% 
  distinct() %>% 
  select(features_continuas)  %>% 
  mutate_all(scale_vble)
nrow(anti_join_audio_charts_complete_scale)

Qué temas perduran mucho en el ranking

Artistas que mas aparecen en el chart

join_audio_charts %>% 
  group_by(artist_name) %>% 
  dplyr::summarise(n = n()) %>% 
  arrange(-n)

Tracks que mas aparecen en el chart

join_audio_charts %>% 
  group_by(track_name, artist_name,external_urls_spotify) %>% 
  dplyr::summarise(n = n()) %>% 
  arrange(-n) %>% 
  select(track_name, n, everything(.))

¿Cuánto tiempo están en un chart?

# cantidad de semanas que estuvieron en el chart

df_charts %>% 
  mutate(week_start=as.Date(week_start),
         week_end = as.Date(week_end),
         week_year = (year(week_start))) %>%
  arrange(Artist, Track_Name) %>% 
  group_by(Artist, Track_Name, URL) %>% 
 dplyr:: summarise( day_in = min(week_start),
             year_in = year(day_in),
             day_max = max(week_end),
             year_max = year(day_max),
             duracion_chart_dias = day_max-day_in,
             duracion_chart_anio = year_max - year_in) %>% 
  arrange(Artist)

#prueba igal de transformacion y test de normalidad

for (i in features_continuas){
   x <- log10(df_chart_w_lyrics[,i])
   x <- shapiro.test(x)
   z <- x$p.value
  print(z)
  }
[1] 1.85241e-21
[1] 1.167246e-23
[1] 7.44622e-11
[1] 1.256851e-30
[1] NaN
[1] 3.287572e-17
NaNs producedError in shapiro.test(x) : sample size must be between 3 and 5000
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIExJQlJFUklBUw0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShzcWxkZikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc2VudGltZW50cikNCmxpYnJhcnkoYXJ1bGVzKQ0KYGBgDQoNCiMgQ0FSR0EgREUgQkFTRVMgDQpgYGB7cn0NCmRmX2FydGlzdCA8LSByZWFkLmNzdigiZGF0YS9kZl9hcnRpc3Rfc2luX2R1cGxpY2Fkb3MuY3N2IikNCmRmX2NoYXJ0c19yYXcgPC0gcmVhZC5jc3YoImRhdGEvZGZfY2hhcnRzX3Npbl9kdXBsaWNhZG9zLmNzdiIpDQpkZl9hdWRpb19mZWF0dXJlc19yYXcgPC0gcmVhZC5jc3YoImRhdGEvYXVkaW9fZmVhdHVyZXNfcGxhbm9fc2luX2R1cGxpY2Fkb3MuY3N2IikNCmRmX2x5cmljcyA8LSByZWFkLmNzdigiZGF0YS9kZl9seXJpY3MuY3N2IikNCg0KYGBgDQoNCg0KIyMgTnVldmEgQ29ycmVjY2nDs24gZHVwbGljYWRvcw0KYGBge3J9DQojIERGIGxpc3RvIHBhcmEgZWwgam9pbiBjb24gY2hyYXRzDQpkZl9hdWRpb19mZWF0dXJlcyA8LSBkZl9hdWRpb19mZWF0dXJlc19yYXcgJT4lIA0KICBncm91cF9ieSh0cmFja19uYW1lLCBleHRlcm5hbF91cmxzX3Nwb3RpZnkpICU+JSANCiAgbXV0YXRlKGFydGlzdF9hbGwgPSBwYXN0ZShhcnRpc3RfbmFtZSwgY29sbGFwc2UgPSAiLHwsIikpICU+JQ0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUoYXJ0aXN0X2tleSA9IHN1YigiLHwsLioiLCAiIiwgYXJ0aXN0X2FsbCkpICU+JSANCiAgZHBseXI6OnNlbGVjdChhcnRpc3RfbmFtZSwgYXJ0aXN0X2FsbCwgYXJ0aXN0X2tleSwgZXZlcnl0aGluZyguKSkgJT4lIA0KICBkaXN0aW5jdChhcnRpc3Rfa2V5LCBleHRlcm5hbF91cmxzX3Nwb3RpZnksIC5rZWVwX2FsbCA9IFQpICU+JSANCiAgYXMuZGF0YS5mcmFtZSgpDQpgYGANCg0KIyBDUkVBQ0lPTiBgY2FudF9tYXJrZXRzYA0KYGBge3J9DQpjb250YXJfbWFya2V0IDwtIGZ1bmN0aW9uKHgpew0KcSA8LSBsZW5ndGgodW5saXN0KHN0cnNwbGl0KHgsIHNwbGl0ID0gIiwiKSkpDQpyZXR1cm4gKHEpDQogIH0NCmRmX2F1ZGlvX2ZlYXR1cmVzJGNhbnRfbWFya2V0cyA8LSBzYXBwbHkoZGZfYXVkaW9fZmVhdHVyZXNbLCJtYXJrZXRzX2NvbmNhdCJdLCBjb250YXJfbWFya2V0KQ0KYGBgDQoNCiMgQ2hhcnRzDQpgYGB7cn0NCiNtZXRyaWNhIGRlIHBvcHVsYXJpZGFkDQpkZl9jaGFydHMgPC0gZGZfY2hhcnRzX3JhdyAlPiUgDQogIGdyb3VwX2J5KEFydGlzdCwgVHJhY2tfTmFtZSwgVVJMKSAlPiUNCiAgZHBseXI6OiBzdW1tYXJpc2Uoc2VtYW5hc19zdW0gPSBuKCksDQogICAgICAgICAgICBzdHJlYW1zX3N1bSA9IChzdW0oU3RyZWFtcywgbmEucm0gPSBUKS8xMF42ICksDQogICAgICAgICAgICBzdHJlYW1zX21pbiA9IChtaW4oU3RyZWFtcykvMTBeNiApLA0KICAgICAgICAgICAgc3RyZWFtc19tYXggPSAobWF4KFN0cmVhbXMpLzEwXjYgKSwNCiAgICAgICAgICAgIHBvc2l0aW9uX2F2ZyA9IG1lYW4oUG9zaXRpb24sIG5hLnJtID0gVCksDQogICAgICAgICAgICBwb3NpdGlvbl9taW4gPSBtaW4oUG9zaXRpb24pLCANCiAgICAgICAgICAgIHBvc2l0aW9uX21heCA9IG1heChQb3NpdGlvbikpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGluZGljYWRvciA9IGFzLm51bWVyaWMoc3RyZWFtc19zdW0qc2VtYW5hc19zdW0vcG9zaXRpb25fYXZnKSApDQoNCmBgYA0KDQoNCiMgVkVDVE9SRVMgREUgRkVBVFVSRVMNCmBgYHtyfQ0KI2ZlYXR1cmVzIHZhciBjb250aW51b3MNCmZlYXR1cmVzX2NvbnRpbnVhcyA8LSBjKCdhY291c3RpY25lc3MnLCAnZGFuY2VhYmlsaXR5JywgJ2R1cmF0aW9uX21zJywgJ2VuZXJneScsICdpbnN0cnVtZW50YWxuZXNzJywgJ2xpdmVuZXNzJywgJ2xvdWRuZXNzJywgJ3NwZWVjaGluZXNzJywgICAndGVtcG8nLCAndmFsZW5jZScsICdjYW50X21hcmtldHMnKQ0KDQojZmVhdHVyZXMgdmFyXyBjYXRlZ8OzcmljYXMNCmZlYXR1cmVzX2NhdGVnb3JpY2FzIDwtIGMoJ2V4cGxpY2l0JywgJ2tleV9uYW1lJywgJ21vZGVfbmFtZScsICJrZXlfbW9kZSIsICJhbGJ1bV90eXBlIikNCg0KYGBgDQoNCg0KDQojIFJJR1RIIEpPSU4gYGF1ZGlvX2ZlYXR1cmVzYCBZIGBjaGFydHNgDQpgYGB7cn0NCiNBcm1hbW9zIHVuIGpvaW4gcGFyYSB0ZW5lciB1bmEgdGFibGEgZGUgY2hhcnRzIGNvbiBsYXMgY2FyYWN0ZXJpc3RpY2FzIGRlIGxhcyBjYW5jaW9uZXMNCiMgZGViZXJpYW4gcXVlZGFyIDIyOTkzIGZpbGFzIGNvbXBsZXRhcw0Kam9pbl9hdWRpb19jaGFydHMgPC0gZGZfYXVkaW9fZmVhdHVyZXMgJT4lIA0KICBzZWxlY3QoImFydGlzdF9uYW1lIiwiYXJ0aXN0X2FsbCIsImFydGlzdF9rZXkiLA0KICAgICAgICAgInRyYWNrX25hbWUiLCAiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiwgImFsYnVtX25hbWUiLCAiYWxidW1fcmVsZWFzZV95ZWFyIiwNCiAgICAgICAgIGFsbF9vZihmZWF0dXJlc19jb250aW51YXMpLCBhbGxfb2YoZmVhdHVyZXNfY2F0ZWdvcmljYXMpKSAlPiUgDQogIHJpZ2h0X2pvaW4oIGRmX2NoYXJ0cywjICU+JQ0KICAgICAgICAgICAgICAgYnkgPSBjKA0KICAgICAgICAgICAgICAgICAidHJhY2tfbmFtZSIgPSAiVHJhY2tfTmFtZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICJhcnRpc3Rfa2V5IiA9IkFydGlzdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICJleHRlcm5hbF91cmxzX3Nwb3RpZnkiID0gIlVSTCIpKQ0KDQojSEFZIENIQVJUUyBRVUUgTk8gVElFTkVOIEZFQVRVUkVTLiBIQVkgUVVFIFRFTkVSTE8gRU4gQ1VFTlRBIFBBUkEgRUwgQU7DgUxJU0lTDQpsaWJyYXJ5KG1pY2UpDQptZC5wYXR0ZXJuKGpvaW5fYXVkaW9fY2hhcnRzLCByb3RhdGUubmFtZXMgPSBUUlVFKQ0KcG9wdWxhcmlkYWRbaXMubmEocG9wdWxhcmlkYWQkaW5kaWNhZG9yKSxdDQoNCmBgYA0KDQoNCg0KI0FncmVnYWNpw7NuIGRlIHRvZGFzIGxhcyBzZW1hbmFzIGVuIGNoYXJ0cw0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQoNCmZlYXR1cmVzX2NvbnRpbnVhcyA8LSBjKCdhY291c3RpY25lc3MnLCAnZGFuY2VhYmlsaXR5JywgJ2R1cmF0aW9uX21zJywgJ2VuZXJneScsICdpbnN0cnVtZW50YWxuZXNzJywgJ2xpdmVuZXNzJywgJ2xvdWRuZXNzJywgJ3NwZWVjaGluZXNzJywgICAndGVtcG8nLCAndmFsZW5jZScsICdjYW50X21hcmtldHMnKQ0KDQpmZWF0dXJlc19jYXRlZ29yaWNhcyA8LSBjKCdleHBsaWNpdCcsICdrZXlfbmFtZScsICdtb2RlX25hbWUnLCAia2V5X21vZGUiLCAiYWxidW1fdHlwZSIpDQoNCmdyb3VwcGluZ19jb2xzIDwtIGMoImFydGlzdF9uYW1lIiwiYXJ0aXN0X2FsbCIsImFydGlzdF9rZXkiLCJ0cmFja19uYW1lIiwiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiwiYWxidW1fbmFtZSIsImFsYnVtX3JlbGVhc2VfeWVhciIpDQoNCm51bWVyaWNfY29sX2NoYXJ0cyA8LSBjKCJQb3NpdGlvbiIsIlN0cmVhbXMiKQ0KDQp3ZWVrX3N0YXJ0IDwtIGMoIndlZWtfc3RhcnQiKQ0KDQpjaGFydF9ncm91cCA8LSBqb2luX2F1ZGlvX2NoYXJ0cyAlPiUgDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoYXJ0aXN0X25hbWUsYXJ0aXN0X2FsbCxhcnRpc3Rfa2V5LHRyYWNrX25hbWUsZXh0ZXJuYWxfdXJsc19zcG90aWZ5LGFsYnVtX25hbWUsYWxidW1fcmVsZWFzZV95ZWFyKQ0KDQoNCmNvbnRpbnVhc19zdW1tYXJpemVkID0gY2hhcnRfZ3JvdXAgJT4lIHN1bW1hcmlzZV9hdChmZWF0dXJlc19jb250aW51YXMsIG1lYW4sIG5hLnJtID0gVFJVRSkNCmNhdGVnb3JpY2FzX3N1bW1hcml6ZXMgPSBjaGFydF9ncm91cCAlPiUgc3VtbWFyaXNlX2F0KGZlYXR1cmVzX2NhdGVnb3JpY2FzLCBmaXJzdCkNCm51bWVyaWNfY2hhcnRzX3N1bW1hcml6ZXMgPSBjaGFydF9ncm91cCAlPiUgc3VtbWFyaXNlKGFjcm9zcyhudW1lcmljX2NvbF9jaGFydHMsIGxpc3QobWluPW1pbixtYXg9bWF4LGF2Zz1tZWFuKSkpDQpjYW50X3NlbWFuYXMgPSBjaGFydF9ncm91cCAlPiUgc3VtbWFyaXNlX2F0KHdlZWtfc3RhcnQsIG5fZGlzdGluY3QpDQpuYW1lcyhjYW50X3NlbWFuYXMkd2Vla19zdGFydCkgPC0gImNhbnRfc2VtYW5hcyINCg0KYWdncmVnYXRpb25fZGYgPC0gY2JpbmQobnVtZXJpY19jaGFydHNfc3VtbWFyaXplcywgY2FudF9zZW1hbmFzWywtYygxOjcpXSxjb250aW51YXNfc3VtbWFyaXplZFssLWMoMTo3KV0sIGNhdGVnb3JpY2FzX3N1bW1hcml6ZXNbLC1jKDE6NyldKQ0KDQpuYW1lcyhhZ2dyZWdhdGlvbl9kZilbbmFtZXMoYWdncmVnYXRpb25fZGYpID09ICd3ZWVrX3N0YXJ0J10gPC0gImNhbnRfc2VtYW5hcyINCg0KY29scyA8LSBuYW1lcyhhZ2dyZWdhdGlvbl9kZikNCm51bWVyaWNfY29scyA8LSBjb2xzW3NhcHBseShhZ2dyZWdhdGlvbl9kZixpcy5udW1lcmljKV0NCg0Kc3VtbWFyeShhZ2dyZWdhdGlvbl9kZlssbnVtZXJpY19jb2xzWzI6bGVuZ3RoKG51bWVyaWNfY29scyldXSkNCg0KDQpgYGANCg0KDQpgYGB7cn0NCmRmX2x5cmljc191bmljYXMgPC0gZGZfbHlyaWNzICU+JSBkaXN0aW5jdChhcnRpc3RfbmFtZSwgdHJhY2tfbmFtZSwgbHlyaWNzKQ0KbnJvdyhkZl9seXJpY3NfdW5pY2FzKQ0KDQpkZl9jaGFydF93X2x5cmljcyA8LSBtZXJnZShqb2luX2F1ZGlvX2NoYXJ0cywgZGZfbHlyaWNzX3VuaWNhcywgYnkueCA9IGMoImFydGlzdF9uYW1lIiwidHJhY2tfbmFtZSIpLCBieS55PSBjKCJhcnRpc3RfbmFtZSIsInRyYWNrX25hbWUiKSwgYWxsLng9VFJVRSwgYWxsLnkgPSBGQUxTRSkNCg0KZGZfY2hhcnRfd19seXJpY3MgPC0gZGZfY2hhcnRfd19seXJpY3NbIWlzLm5hKGRmX2NoYXJ0X3dfbHlyaWNzJGx5cmljcyksXQ0KDQpgYGANCg0KIyBjb250YXIgbWFsYXMgcGFsYWJyYXMNCmBgYHtyfQ0KDQpiYWRfd29yZHMgPC0gYygpDQoNCmJhZF93b3JkcyA8LSBhcHBlbmQoYmFkX3dvcmRzLCB1bmlxdWUodG9sb3dlcihsZXhpY29uOjpwcm9mYW5pdHlfemFjX2FuZ2VyKSkpDQoNCmJhZF93b3JkcyA8LSBhcHBlbmQoYmFkX3dvcmRzLCB1bmlxdWUodG9sb3dlcihsZXhpY29uOjpwcm9mYW5pdHlfYWx2YXJleikpKQ0KDQpiYWRfd29yZHMgPC0gYXBwZW5kKGJhZF93b3JkcywgdW5pcXVlKHRvbG93ZXIobGV4aWNvbjo6cHJvZmFuaXR5X2Fycl9iYWQpKSkNCg0KYmFkX3dvcmRzIDwtIGFwcGVuZChiYWRfd29yZHMsIHVuaXF1ZSh0b2xvd2VyKGxleGljb246OnByb2Zhbml0eV9yYWNpc3QpKSkNCg0KYmFkX3dvcmRzIDwtIGFwcGVuZChiYWRfd29yZHMsIHVuaXF1ZSh0b2xvd2VyKGxleGljb246OnByb2Zhbml0eV9iYW5uZWQpKSkNCg0KYmFkX3dvcmRzIDwtIHVuaXF1ZShiYWRfd29yZHMpDQoNCg0KY29udGFyX2JhZF93b3JkcyA8LSBmdW5jdGlvbih4KXsNCiAgeCA8LSBwcm9mYW5pdHkoeCxwcm9mYW5pdHlfbGlzdCA9IGJhZF93b3JkcykNCiAgcSA8LSBzdW0oeCRwcm9mYW5pdHlfY291bnQpDQogIHJldHVybiAocSkNCiAgfQ0KZGZfY2hhcnRfd19seXJpY3MkY2FudF9iYWRfd29yZHMgPC0gc2FwcGx5KGRmX2NoYXJ0X3dfbHlyaWNzWywibHlyaWNzIl0sIGNvbnRhcl9iYWRfd29yZHMpDQoNCg0KZGZfY2hhcnRfd19seXJpY3Nfb25seV9leHBsaWNpdCA8LSBkZl9jaGFydF93X2x5cmljc1tkZl9jaGFydF93X2x5cmljcyRleHBsaWNpdD09VFJVRSAmIGRmX2NoYXJ0X3dfbHlyaWNzJGNhbnRfYmFkX3dvcmRzID4gMCwgXQ0KDQpoaXN0KGRmX2NoYXJ0X3dfbHlyaWNzX29ubHlfZXhwbGljaXQkY2FudF9iYWRfd29yZHMpDQoNCg0KI2NyZW8gdmFycyBjYXRlZ8OzcmljYXMNCmRmX2NoYXJ0X3dfbHlyaWNzX29ubHlfZXhwbGljaXQkbml2ZWxfcHV0ZWFkYSA8LSBjdXQoZGZfY2hhcnRfd19seXJpY3Nfb25seV9leHBsaWNpdCRjYW50X2JhZF93b3JkcywgYnJlYWtzID0gYygwLDEwLDIwLDUwLEluZiksIGxhYmVscz1jKCJleHBfYmFqbyIsImV4cF9wb2NvIiwiZXhwX2FsdG8iLCJleHBfbXV5X2FsdG8iKSkNCg0KZGZfY2hhcnRfd19seXJpY3Nfb25seV9leHBsaWNpdCRuaXZlbF9yYW5raW5nIDwtIGN1dChkZl9jaGFydF93X2x5cmljc19vbmx5X2V4cGxpY2l0JHBvc2l0aW9uX2F2ZywgYnJlYWtzID0gYygxLDEwMCxJbmYpLCBsYWJlbHM9YygiY2hhcnRfMWExMDAiLCJjaGFydF8xMDBhMjAwIikpDQoNCmRmX2NoYXJ0X3dfbHlyaWNzX29ubHlfZXhwbGljaXQkbml2ZWxfcG9wdWxhcmlkYWQgPC0gY3V0KHNxcnQoZGZfY2hhcnRfd19seXJpY3Nfb25seV9leHBsaWNpdCRjYW50X2JhZF93b3JkcyksIGJyZWFrcyA9IGMoMCwxMCwyMCw1MCxJbmYpLCBsYWJlbHM9YygicG9wX2Jham8iLCJwb3BfcG9jbyIsInBvcF9hbHRvIiwicG9wX211eSBhbHRvIikpDQoNCnRyYW5zYWN0aW9ucyA8LSBhcyhhcy5kYXRhLmZyYW1lKGFwcGx5KGRmX2NoYXJ0X3dfbHlyaWNzX29ubHlfZXhwbGljaXQsIDIsIGFzLmZhY3RvcikpLCAidHJhbnNhY3Rpb25zIikNCnJ1bGVzID0gYXByaW9yaSh0cmFuc2FjdGlvbnMsIHBhcmFtZXRlcj1saXN0KHRhcmdldD0icnVsZXMiLCBjb25maWRlbmNlPTAuMjUsIHN1cHBvcnQ9MC4xKSkNCnJ1bGVzLnN1YiA8LSBzdWJzZXQocnVsZXMsIHN1YnNldCA9IGxocyAlcGluJSAibml2ZWxfcHV0ZWFkYSIgJiByaHMgJXBpbiUgIm5pdmVsX3JhbmtpbmciKQ0KaW5zcGVjdChoZWFkKHNvcnQocnVsZXMuc3ViLCBieSA9ICJsaWZ0IiwgZGVjcmVhc2luZyA9IFRSVUUpLDEwKSkNCg0KIyBkaXNjcmV0aXphY2lvbiBjb250aW51YXMgeSBzZWxlY2Npb24gZGUgdmFyaWFibGVzDQojIGlkZW50aWZpY2FyIHBhbGFicmFzIGV4cGzDrXQNCg0KYGBgDQoNCg0KDQojIEhJU1RPR1JBTUFTIFkgQkFSUExPVFMgREUgVkFSSUFCTEVTDQpgYGB7cn0NCg0KIyNoaXN0b2dyYW1hIGRlIGxhcyB2YXJpYWJsZXMgY29udGludWFzIGRlIGF1ZGlvX2ZlYXR1cmVzDQoNCmZvciAoaSBpbiBmZWF0dXJlc19jb250aW51YXMpew0KDQogIGhpc3QoZGZfYXVkaW9fZmVhdHVyZXNbLGldLCBtYWluID0gcGFzdGUoIkhpc3RvZ3JhbWEgZGUiLCBpLCAiKGFsbCBkYXRhKSIpLCB4bGFiID0gaSkNCiAgYWJsaW5lKHYgPSBtZWFuKGRmX2F1ZGlvX2ZlYXR1cmVzWyxpXSwgbmEucm0gPSBUUlVFKSAsIGNvbD0icmVkIikNCiAgYWJsaW5lKHYgPSBtZWRpYW4oZGZfYXVkaW9fZmVhdHVyZXNbLGldLCBuYS5ybSA9IFRSVUUpICwgY29sPSJibHVlIikNCiAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIk1lZGlhIiwgIk1lZGlhbmEiKSwgY29sPWMoInJlZCIsICJibHVlIiksIGx0eSA9MSkNCg0KfQ0KDQojZGl2aWRvIGxvcyBmZWF0dXJlcyBwb3Igc3UgZGlzdHJpYnVjacOzbg0KZmVhdHVyZXNfY29udGludWFzX21lZGlhIDwtIGMoJ2RhbmNlYWJpbGl0eScsICd0ZW1wbycsICd2YWxlbmNlJykNCg0KZmVhdHVyZXNfY29udGludWFzX21lZGlhbmEgPC0gYygnYWNvdXN0aWNuZXNzJywgJ2R1cmF0aW9uX21zJywgJ2VuZXJneScsICdpbnN0cnVtZW50YWxuZXNzJywgJ2xpdmVuZXNzJywgJ2xvdWRuZXNzJywgJ3NwZWVjaGluZXNzJywgJ2NhbnRfbWFya2V0cycpDQoNCg0KIyNoaXN0b2dyYW1hIGRlIGxhcyB2YXJpYWJsZXMgY29udGludWFzIGRlIGNoYXJ0cw0KZm9yIChpIGluIGMoZmVhdHVyZXNfY29udGludWFzLCAiU3RyZWFtcyIpKXsNCg0KICBoaXN0KGpvaW5fYXVkaW9fY2hhcnRzWyxpXSwgbWFpbiA9IHBhc3RlKCJIaXN0b2dyYW1hIGRlIiwgaSwgICIoY2hhcnRzKSIpLCB4bGFiID0gaSkNCiAgYWJsaW5lKHYgPSBtZWFuKGpvaW5fYXVkaW9fY2hhcnRzWyxpXSwgbmEucm0gPSBUUlVFKSAsIGNvbD0icmVkIikNCiAgYWJsaW5lKHYgPSBtZWRpYW4oam9pbl9hdWRpb19jaGFydHNbLGldLCBuYS5ybSA9IFRSVUUpICwgY29sPSJibHVlIikNCg0KfQ0KDQojZGl2aWRvIGZlYXR1cmVzIGRlIGNoYXJ0cyBzZWfDum4gc3UgZGlzdHJpYnVjacOzbg0KYXVkaW9fY2hhcnRzX2NvbnRpbnVhc19tZWRpYSA8LSBjKCdkdXJhdGlvbl9tcycsICd2YWxlbmNlJykNCg0KYXVkaW9fY2hhcnRzX2NvbnRpbnVhc19tZWRpYW5hIDwtIGMoJ2RhbmNlYWJpbGl0eScsICdhY291c3RpY25lc3MnLCAndGVtcG8nLCAnZW5lcmd5JywgJ2luc3RydW1lbnRhbG5lc3MnLCAnbGl2ZW5lc3MnLCAnbG91ZG5lc3MnLCAnc3BlZWNoaW5lc3MnLCAnY2FudF9tYXJrZXRzJywgIlN0cmVhbXMiKQ0KDQoNCiMjbWVkaWRhcyByZXN1bWVuIHkgYmFycGxvdHMgZGUgbGFzIHZhcmlhYmxlcyBjYXRlZ29yaWNhcyBhdWRpb19mZWF0dXJlcw0KZm9yKGkgaW4gZmVhdHVyZXNfY2F0ZWdvcmljYXMpew0KDQogIGJhcnBsb3Qoc29ydCh0YWJsZShkZl9hdWRpb19mZWF0dXJlc1ssaV0pLGRlY3JlYXNpbmcgPSBUKSwgbGFzPTIsIA0KICAgICAgICAgIG1haW4gPSBwYXN0ZSgiQmFycGxvdCBkZSIsIGksICIoYWxsIGRhdGEpIikpDQogICMgcGllKHRhYmxlKGRmX2ZlYXR1cmVzX2NhdGVnb3JpY29zWyxpXSkpDQp9DQoNCg0KDQojI21lZGlkYXMgcmVzdW1lbiB5IGJhcnBsb3RzIGRlIGxhcyB2YXJpYWJsZXMgY2F0ZWdvcmljYXMgam9pbl9hdWRpb19jaGFydHMNCg0KZm9yKGkgaW4gZmVhdHVyZXNfY2F0ZWdvcmljYXMpew0KICANCiAgYmFycGxvdChzb3J0KHRhYmxlKGpvaW5fYXVkaW9fY2hhcnRzWyxpXSksZGVjcmVhc2luZyA9IFQpLCBsYXM9MiwgDQogICAgICAgICAgbWFpbiA9IHBhc3RlKCJCYXJwbG90IGRlIiwgaSwgIihjaGFydHMpIikpDQogICMgcGllKHRhYmxlKGRmX2ZlYXR1cmVzX2NhdGVnb3JpY29zWyxpXSkpDQp9DQoNCg0KYGBgDQoNCiMjIEFuYWxpc2lzIGRlIGxhIHZhcmlhYmxlIGBtYXJrZXRzX2NvbmNhdGANCg0KYGBge3J9DQojSGFnbyB1biBqb2luIGFsIHJldsOpcyANCg0KZGZfY2hhcnRfdG9qb2luIDwtIGRmX2NoYXJ0c1ssYygiVHJhY2tfTmFtZSIsICJBcnRpc3QiLCAiVVJMIildDQpkZl9jaGFydF90b2pvaW4kaXNpbmNoYXJ0IDwtIDENCmRmX2F1ZGlvX2ZlYXR1cmVzX3Rvam9pbiA8LSBkZl9hdWRpb19mZWF0dXJlc1ssIGMoInRyYWNrX25hbWUiLCJhcnRpc3Rfa2V5IiwiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiwibWFya2V0c19jb25jYXQiKV0NCg0Kam9pbl9iYXJwbG90IDwtIGRmX2F1ZGlvX2ZlYXR1cmVzX3Rvam9pbiAlPiUgDQogIHNlbGVjdCgidHJhY2tfbmFtZSIsImFydGlzdF9rZXkiLCJleHRlcm5hbF91cmxzX3Nwb3RpZnkiLCJtYXJrZXRzX2NvbmNhdCIpICU+JSANCiAgbGVmdF9qb2luKCBkZl9jaGFydF90b2pvaW4gJT4lDQogICAgICAgICAgICAgICBzZWxlY3QoIlRyYWNrX05hbWUiLCAiQXJ0aXN0IiwgIlVSTCIsImlzaW5jaGFydCIpLA0KICAgICAgICAgICAgICAgYnkgPSBjKA0KICAgICAgICAgICAgICAgICAidHJhY2tfbmFtZSIgPSAiVHJhY2tfTmFtZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICJhcnRpc3Rfa2V5IiA9IkFydGlzdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICJleHRlcm5hbF91cmxzX3Nwb3RpZnkiID0gIlVSTCIpKQ0KDQoNCmpvaW5fYmFycGxvdCRpc2luY2hhcnRbaXMubmEoam9pbl9iYXJwbG90JGlzaW5jaGFydCldIDwtIDANCg0Kam9pbl9iYXJwbG90JGlzaW5jaGFydCA8LSBmYWN0b3Ioam9pbl9iYXJwbG90JGlzaW5jaGFydCkNCg0KdGFibGFfaXNpbmNoYXJ0IDwtIHRhYmxlKHVubGlzdChsYXBwbHkoam9pbl9iYXJwbG90W2pvaW5fYmFycGxvdCRpc2luY2hhcnQ9PTEsIm1hcmtldHNfY29uY2F0Il0sIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4KSwgJywnKSkpKQ0KDQp0YWJsYV9ub3RpbmNoYXJ0IDwtIHRhYmxlKHVubGlzdChsYXBwbHkoam9pbl9iYXJwbG90W2pvaW5fYmFycGxvdCRpc2luY2hhcnQ9PTAsIm1hcmtldHNfY29uY2F0Il0sIGZ1bmN0aW9uKHgpIHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4KSwgJywnKSkpKQ0KDQphbGxfY291bnRyaWVzIDwtIG5hbWVzKHRhYmxhX2lzaW5jaGFydCkNCg0KeGxhYnMgPC0gcGFzdGUocGFzdGUoaGVhZChhbGxfY291bnRyaWVzLDMpLCBjb2xsYXBzZSA9ICIsIiksIi4uLiIscGFzdGUodGFpbChhbGxfY291bnRyaWVzLDMpLGNvbGxhcHNlID0gIiwiKSwiKElTTy1Db2RlcyBkZSBQYWlzZXMpIiwgIGNvbGxhcHNlID0gIiwiKQ0KDQpvcHRpb25zKHNjaXBlbj05OTkpDQpwYXIobWZyb3cgPSBjKDEsMiksIGxhcz0xLCBtYXI9YygzLDMsNSwzKSwgb21hPWMoMCwxLDEsMSkpDQpiYXJwbG90KHNvcnQodGFibGFfaXNpbmNoYXJ0LCBkZWNyZWFzaW5nID0gVFJVRSksIG5hbWVzLmFyZz0iIiwgbWFpbiA9J0VuIENoYXJ0cycsY29sPXJnYigwLjIsMC40LDAuNiwwLjYpLHhsYWIgPSAiUGFpc2VzIChJU08tQ29kZXMpIikNCiMgbXRleHQoc2lkZSA9IDEsIHRleHQgPSB4bGFicywgbGluZSA9IDEpDQpiYXJwbG90KHNvcnQodGFibGFfbm90aW5jaGFydCwgZGVjcmVhc2luZyA9IFRSVUUpLCBuYW1lcy5hcmcgPSAiIiwgbWFpbj0nRnVlcmEgZGUgQ2hhcnRzJyxjb2w9cmdiKDAuMiwwLjQsMC42LDAuNiksIHhsYWIgPSAiUGFpc2VzIChJU08tQ29kZXMpIikNCm10ZXh0KHNpZGUgPSAxLCB0ZXh0ID0geGxhYnMsIGxpbmUgPSAxLCBhZGogPSAyKQ0KbXRleHQoIkZyZWN1ZW5jaWEgZGUgbWVyY2Fkb3MgaGFiaWxpdGFkb3MiLCBzaWRlID0gMywgbGluZSA9IC0xLCBvdXRlciA9IFRSVUUsIGNleCA9IDEuMywgZm9udCA9MiApDQojIG10ZXh0KCJQYWlzZXMgKElTTy1Db2RlcykiLCBzaWRlID0gMywgbGluZSA9IC0yNSwgb3V0ZXIgPSBUUlVFKQ0KYGBgDQoNCg0KIyBDT1JSRUxBQ0lPTkVTDQpgYGB7cn0NCiNjb3JyZWxhY2lvbmVzIGVuIGF1ZGlvIGZlYXR1cmVzDQp4IDwtIGNvcihkZl9hdWRpb19mZWF0dXJlc1ssYyhmZWF0dXJlc19jb250aW51YXNfbWVkaWEsIGZlYXR1cmVzX2NvbnRpbnVhc19tZWRpYW5hKV0sICB1c2UgPSAgImNvbXBsZXRlLm9icyIpDQpjb3JycGxvdCh4LCB0eXBlID0gInVwcGVyIiwgdGl0bGUgPSAiQ29ycmVsYWNpb24gZGUgYXRyaWJ1dG9zIGRlIGF1ZGlvX2ZlYXR1cmVzIiwgbWFyPWMoMCwwLDEsMCksIG1ldGhvZD0ibnVtYmVyIiAsbnVtYmVyLmNleD0wLjcpDQpgYGANCg0KDQpgYGB7cn0NCg0KI2NvcnJlbGFjaW9uZXMgZW4gY2hhcnRzDQp4IDwtIGNvcihzY2FsZShqb2luX2F1ZGlvX2NoYXJ0c1ssYyhhdWRpb19jaGFydHNfY29udGludWFzX21lZGlhLCBhdWRpb19jaGFydHNfY29udGludWFzX21lZGlhbmEpXSksIHVzZSA9ICAiY29tcGxldGUub2JzIikNCmNvcnJwbG90KHgsIHR5cGUgPSAidXBwZXIiLCB0aXRsZSA9ICJDb3JyZWxhY2lvbiBkZSBhdHJpYnV0b3MgZGUgbG9zIENoYXJ0cyIsIG1hcj1jKDAsMCwxLDApLCBtZXRob2Q9Im51bWJlciIsIG51bWJlci5jZXg9MC43ICkNCmBgYA0KDQpgYGB7cn0NCg0KI2NoaTIgdGVzdCAjY29uIG4gZ3JhbmRlIG5vIHNlIHB1ZWRlIHVzYXIgZXN0ZSB0ZXN0DQp0YWJsYV9rZXlfYWxidW0gPC0gdGFibGUoZGZfYXVkaW9fZmVhdHVyZXMka2V5X25hbWUsIGRmX2F1ZGlvX2ZlYXR1cmVzJGFsYnVtX3R5cGUpDQpjYXQoIlRhYmxhIGRlIGNvbnRpZ2VuY2lhIGVudHJlIGtleSB5IGFsYnVtIHR5cGVcbiIpDQp0YWJsYV9rZXlfYWxidW0NCmNoaXNxLnRlc3QodGFibGFfa2V5X2FsYnVtKQ0KYGBgDQoNCg0KIyBTRVNHTyBERSBWQVJJQUJMRVMgDQoNCiMjIEJveHBsb3RzIFZhcmlhYmxlcyBOdW3DqXJpY2FzIHNpbiBmaWx0cmFyIG91dGxpZXJzDQpgYGB7cn0NCiNkaXZpZG8gbG9zIGZlYXR1cmVzIHBvciBzdSBkaXN0cmlidWNpw7NuDQpmZWF0dXJlc19jb250aW51YXNfbWVkaWEgPC0gYygnZGFuY2VhYmlsaXR5JywgJ3RlbXBvJywgJ3ZhbGVuY2UnKQ0KDQpmZWF0dXJlc19jb250aW51YXNfbWVkaWFuYSA8LSBjKCdhY291c3RpY25lc3MnLCAnZHVyYXRpb25fbXMnLCAnZW5lcmd5JywgJ2luc3RydW1lbnRhbG5lc3MnLCAnbGl2ZW5lc3MnLCAnbG91ZG5lc3MnLCAnc3BlZWNoaW5lc3MnLCAnY2FudF9tYXJrZXRzJykNCg0KYWxsX2ZlYXR1cmVzIDwtIGMoZmVhdHVyZXNfY29udGludWFzX21lZGlhLCBmZWF0dXJlc19jb250aW51YXNfbWVkaWFuYSkNCg0KcGFyKG1mcm93PWMoNCwzKSkNCmZvciAoZmVhdHVyZSBpbiBhbGxfZmVhdHVyZXMpew0KICBib3hwbG90KGRmX2F1ZGlvX2ZlYXR1cmVzWyxmZWF0dXJlXSwgbGFzPTIsIGhvcml6b250YWw9VCwgbWFpbj1mZWF0dXJlKQ0KfQ0KYGBgDQoNCkNvbiBleGNlcGNpw7NuIGRlIHZhbGVuY2UgZWwgcmVzdG8gZGUgbGFzIGZlYXR1cmVzIHBvc2XDrWFuIGNpZXJ0byBzZXNnby4gU2UgZGVjaWRpw7MgdHJhbnNmb3JtYXIgbGFzIHZhcmlhYmxlcyBxdWUgbWF5b3Igc2VzZ28gcG9zZcOtYW46IGR1cmF0aW9uX21zLCBpbnN0cnVtZW50YWxuZXNzLCBsaXZlbmVzcywgc3BlZWNoaW5lc3MgY29tbyBtw6l0b2RvIGRlIGNvcnJlZ2lyIGxhIGRpc3RyaWJ1Y2nDs24geSBhY2hpY2FyIGxhIGNhbnRpZGFkIGRlIG91dGxpZXJzLiBMYSB2YXJpYWJsZSBsb3VkbmVzc19yZWdfaW1wIG5vIGZ1ZSBtb2RpZmljYWRhIGRlYmlkbyBhIHF1ZSBhbCBzZXIgbmVnYXRpdmEgDQoNCg0KYGBge3J9DQojICJkYW5jZWFiaWxpdHksdGVtcG8sdmFsZW5jZSxhY291c3RpY25lc3MsZHVyYXRpb25fbXMsZW5lcmd5LGluc3RydW1lbnRhbG5lc3MsbGl2ZW5lc3Msc3BlZWNoaW5lc3MsY2FudF9tYXJrZXRzIg0KDQojc2VzZ29zIGQgbGFzIHZhcmlhYmxlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0Kc29ydChhcHBseShkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZXNfY29udGludWFzXSwgTUFSR0lOID0gMiwgZnVuY3Rpb24oeCl7ICgzKiAobWVhbih4LG5hLnJtID0gVCktbWVkaWFuKHgsIG5hLnJtID0gVCkpKS9zZCh4LCBuYS5ybSA9IFQpfSApKQ0KDQp2YXJpYWJsZXNfc2VzZ28gPC0gdW5saXN0KHN0cnNwbGl0KCJhY291c3RpY25lc3MsZHVyYXRpb25fbXMsaW5zdHJ1bWVudGFsbmVzcyxsaXZlbmVzcyxzcGVlY2hpbmVzcyxjYW50X21hcmtldHMsZW5lcmd5IiwgIiwiKSkNCg0KZGZfc2VzZ2FkYXMgPC0gZGZfYXVkaW9fZmVhdHVyZXNbLHZhcmlhYmxlc19zZXNnb10NCg0KbG9nYXJpdG1vX2FqdXN0YWRvID0gZnVuY3Rpb24oeCxkZWx0YSl7DQogIGlmICh4PT0wLjApew0KICAgIHJldHVybihsb2coMC4wMCtkZWx0YSwgYmFzZSA9IDEwKSkNCiAgfWVsc2V7DQogICAgcmV0dXJuKGxvZyh4LCBiYXNlID0gMTApKQ0KICB9DQp9DQoNCmRlbHRhIDwtIDEwXigtNikNCg0KZGZfc2VzZ2FkYXNfbG9nX2FkanVzdCA8LSBkYXRhLmZyYW1lKGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzWyx2YXJpYWJsZXNfc2VzZ29dLCBNQVJHSU4gPSBjKDEsMiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpDQojIG5hbWVzKGRmX3Nlc2dhZGFzX2xvZ19hZGp1c3QpIDwtIHBhc3RlKG5hbWVzKGRmX3Nlc2dhZGFzKSwgIl9sb2ciLCBzZXA9IiIpDQpuYW1lcyhkZl9zZXNnYWRhc19sb2dfYWRqdXN0KSA8LSBuYW1lcyhkZl9zZXNnYWRhcykNCg0KZGZfZGF0b3MgPC0gY2JpbmQoZGZfc2VzZ2FkYXMsIGRmX3Nlc2dhZGFzX2xvZ19hZGp1c3QpDQoNCg0KDQphIDwtIGRmX3Nlc2dhZGFzDQpiIDwtIGRmX3Nlc2dhZGFzX2xvZ19hZGp1c3QNCm5hbWVzKGIpIDwtIHBhc3RlKG5hbWVzKGRmX3Nlc2dhZGFzKSwgIl9sb2ciLCBzZXA9IiIpDQptZXJnZWQgPC0gY2JpbmQoYSxiKQ0KDQptZXJnZWQgPC0gbWVyZ2VkWywgb3JkZXIobmFtZXMobWVyZ2VkKSldDQoNCnJvdW5kKHNvcnQoYXBwbHkobWVyZ2VkLCBNQVJHSU4gPSAyLCBmdW5jdGlvbih4KXsgKDMqIChtZWFuKHgsbmEucm0gPSBUKS1tZWRpYW4oeCwgbmEucm0gPSBUKSkpL3NkKHgsIG5hLnJtID0gVCl9KSksMikNCg0KYGBgDQoNCg0KYGBge3J9DQoNCnZhcmlhYmxlc19wbG90IDwtIHVubGlzdChzdHJzcGxpdCgiZHVyYXRpb25fbXMiLCAiLCIpKQ0KdmFyaWFibGVzX3Bsb3QgPC0gYXBwZW5kKHZhcmlhYmxlc19wbG90LHBhc3RlKHZhcmlhYmxlc19wbG90LCJfbG9nIiwgc2VwPSIiKSkNCnZhcmlhYmxlc19wbG90IDwtIHZhcmlhYmxlc19wbG90W29yZGVyKHZhcmlhYmxlc19wbG90KV0NCnBsb3RlYXIgPC0gbWVyZ2VkWyx2YXJpYWJsZXNfcGxvdF0NCg0KcGFyKG1mcm93ID0gYygxLDIpKQ0KZm9yIChjb2wgaW4gbmFtZXMocGxvdGVhcikpew0KICBoaXN0KHBsb3RlYXJbLGNvbF0sIGJyZWFrcz0iRkQiLCBtYWluPWNvbCwgeGxhYj0iIikNCn0NCg0KYGBgDQoNCg0KDQpgYGB7cn0NCnN1bW1hcnkoZGZfYXVkaW9fZmVhdHVyZXNbLGFsbF9mZWF0dXJlc10pDQpgYGANCmBgYHtyfQ0KDQoNCmhpc3QobG9nKGRmX2F1ZGlvX2ZlYXR1cmVzJGR1cmF0aW9uX21zKSkNCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KDQp0cmFuc2Zvcm1hY2lvbiA8LSBjKCdpbnN0cnVtZW50YWxuZXNzJywnbG91ZG5lc3MnLCdsaXZlbmVzcycsJ3NwZWVjaGluZXNzJywgJ2R1cmF0aW9uX21zJykNCg0KbG9nYXJpdG1vX2FqdXN0YWRvID0gZnVuY3Rpb24oeCxkZWx0YSl7DQogIGlmICh4PD0wLjApew0KICAgIHJldHVybihsb2coMC4wMCtkZWx0YSwgYmFzZSA9IDEwKSkNCiAgfWVsc2V7DQogICAgcmV0dXJuKGxvZyh4LCBiYXNlID0gMTApKQ0KICB9DQp9DQoNCmRlbHRhIDwtIDEwXigtNikNCg0KcGFyKG1mcm93PWMoMiw0KSkNCmZvciAoZmVhdHVyZSBpbiB0cmFuc2Zvcm1hY2lvbil7DQogIGhpc3QoZGZfYXVkaW9fZmVhdHVyZXNbLGZlYXR1cmVdLCBtYWluPWZlYXR1cmUpDQp9DQpmb3IgKGZlYXR1cmUgaW4gdHJhbnNmb3JtYWNpb24pew0KICBoaXN0KHVubGlzdChsYXBwbHkoZGZfYXVkaW9fZmVhdHVyZXNbLGZlYXR1cmVdLCBmdW5jdGlvbih4KSBsb2dhcml0bW9fYWp1c3RhZG8oeCxkZWx0YSkpKSwgbWFpbj1wYXN0ZShmZWF0dXJlLCJsb2ciLCBzZXA9Il8iKSkNCn0NCmBgYA0KDQpgYGB7cn0NCg0KaW52X3NxcnRfYWp1c3RhZGEgPSBmdW5jdGlvbih4LCBkZWx0YSl7DQogIGlmICh4PT0wLjApew0KICAgIHJldHVybigxL3NxcnQoeCtkZWx0YSkpDQogIH1lbHNlew0KICAgIHJldHVybigxL3NxcnQoeCkpDQogIH0NCn0NCg0KDQpkZWx0YSA8LSAxMF4oLTYpDQoNCnBhcihtZnJvdz1jKDIsNCkpDQpmb3IgKGZlYXR1cmUgaW4gdHJhbnNmb3JtYWNpb24pew0KICBoaXN0KGRmX2F1ZGlvX2ZlYXR1cmVzWyxmZWF0dXJlXSwgbWFpbj1mZWF0dXJlKQ0KfQ0KZm9yIChmZWF0dXJlIGluIHRyYW5zZm9ybWFjaW9uKXsNCiAgaGlzdCh1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzWyxmZWF0dXJlXSwgZnVuY3Rpb24oeCkgaW52X3NxcnRfYWp1c3RhZGEoeCxkZWx0YSkpKSwgbWFpbj1wYXN0ZShmZWF0dXJlLCJpbnZfc3F0Iiwgc2VwPSJfIikpDQp9DQoNCg0KDQpgYGANCg0KDQpgYGB7cn0NCg0KDQpwYXIobWZyb3c9YygyLDQpKQ0KZm9yIChmZWF0dXJlIGluIHRyYW5zZm9ybWFjaW9uKXsNCiAgaGlzdChkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZV0sIG1haW49ZmVhdHVyZSkNCn0NCmZvciAoZmVhdHVyZSBpbiB0cmFuc2Zvcm1hY2lvbil7DQogIGhpc3Qoc3FydChkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZV0pLCBtYWluPXBhc3RlKGZlYXR1cmUsInNxcnQiLCBzZXA9Il8iKSkNCn0NCg0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KcGFyKG1mcm93ID0gYygyLDEpKSANCmhpc3QoZGZfYXVkaW9fZmVhdHVyZXNbLCdsb3VkbmVzc19yZWdfaW1wJ10sIG1haW49J2xvdWRuZXNzJywgeGxhYj0iIikNCiNoaXN0KHNxcnQoZGZfYXVkaW9fZmVhdHVyZXNbLCdsb3VkbmVzc19yZWdfaW1wJ10pLCBtYWluPSAnbG91ZG5lc3Nfc3FydCcsIHhsYWI9IiIpDQpib3hwbG90KGRmX2F1ZGlvX2ZlYXR1cmVzWywnbG91ZG5lc3NfcmVnX2ltcCddLCBob3Jpem9udGFsID0gVCkNCiNib3hwbG90KHNxcnQoZGZfYXVkaW9fZmVhdHVyZXNbLCdsb3VkbmVzc19yZWdfaW1wJ10pLCBob3Jpem9udGFsID0gVCkNCg0KDQoNCg0KDQpgYGANCg0KYGBge3J9DQpmaXQgPC0gbG0obG91ZG5lc3N+ZW5lcmd5K2Fjb3VzdGljbmVzcywgZGF0YT1kZl9hdWRpb19mZWF0dXJlcykNCg0KbW9kZWxvIDwtIGZpdCRjb2VmZmljaWVudHMNCg0KZGZfYXVkaW9fZmVhdHVyZXMkbG91ZG5lc3NfcmVnX2ltcCA8LSBkZl9hdWRpb19mZWF0dXJlcyRsb3VkbmVzcw0KDQpYIDwtIGRmX2F1ZGlvX2ZlYXR1cmVzW2RmX2F1ZGlvX2ZlYXR1cmVzJGxvdWRuZXNzPjAsIGMoJ2VuZXJneScsICJhY291c3RpY25lc3MiKV0NCg0KZGZfYXVkaW9fZmVhdHVyZXMkbG91ZG5lc3NfcmVnX2ltcFtkZl9hdWRpb19mZWF0dXJlcyRsb3VkbmVzcz4wXSA8LSBtb2RlbG9bMV0rbW9kZWxvWzJdKlhbLDFdK21vZGVsb1szXSpYWywyXQ0KDQpzdW1tYXJ5KGRmX2F1ZGlvX2ZlYXR1cmVzWyxjKCJsb3VkbmVzcyIsICJsb3VkbmVzc19yZWdfaW1wIildKQ0KDQpzdW1tYXJ5KGZpdCkNCmBgYA0KDQoNCg0KYGluc3RydW1lbnRhbG5lc3NgIHRpZW5lIG11Y2hvIHNlc2dvIGxhIHZhcmlhYmxlLiBTZSB2YSBhIHJlY3VycmlyIGEgdW5hIGxvZ2FyaXRtaXphY2nDs24gZGUgbGEgdmFyaWFibGUsIHByZXZpYSB0cmFuc2Zvcm1hY2nDs24gZGVsIGRvbWluaW8sIGhhY2llbmRvIHF1ZSBsb3MgdmFsb3JlcyBxdWUgc29uIDAsIHNlYW4gZW4gcmVhbGlkYWQgMC4wMDAwMDAxICANCg0KYGBge3J9DQpsb2dhcml0bW9fYWp1c3RhZG8gPSBmdW5jdGlvbih4LGRlbHRhKXsNCiAgaWYgKHg9PTAuMCl7DQogICAgcmV0dXJuKGxvZyh4K2RlbHRhLCBiYXNlID0gMTApKQ0KICB9ZWxzZXsNCiAgICByZXR1cm4obG9nKHgsIGJhc2UgPSAxMCkpDQogIH0NCn0NCg0KZGVsdGEgPC0gMTBeKC02KQ0KDQpkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCA8LSB1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpDQoNCnBhcihtZnJvdyA9YygyLDIpKQ0KaGlzdChkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzLCBtYWluPSJpbnNydW1lbnRhbG5lc3MiLCB4bGFiPSIiKQ0KaGlzdCh1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpLCBtYWluPSdpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCcsIHlsaW0gPSBjKDAsMTMwNTAwKSwgeGxhYiA9ICIiKQ0KYm94cGxvdChkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzLCBtYWluPSIiLCBob3Jpem9udGFsID0gVCkNCmJveHBsb3QodW5saXN0KGxhcHBseShkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzLCBmdW5jdGlvbih4KSBsb2dhcml0bW9fYWp1c3RhZG8oeCxkZWx0YSkpKSwgbWFpbj0iIiwgaG9yaXpvbnRhbD1UKQ0KIyBoaXN0KGxvZygxL3NxcnQoZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzcyswLjAwMDAxKSksbWFpbj0nbG9nKHNxcnQoeCspKScsIHlsaW09YygwLDEzMDUwMCksIHhsYWIgPSAiIikNCg0KYGBgDQoNCsK/RXMgw7p0aWwgZXN0YSB0cmFuc2Zvcm1hY2nDs24/IA0KDQpgYGB7cn0NCg0KZGVsdGEgPC0gMTBeKC02KQ0KDQpkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCA8LSB1bmxpc3QobGFwcGx5KGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsIGZ1bmN0aW9uKHgpIGxvZ2FyaXRtb19hanVzdGFkbyh4LGRlbHRhKSkpDQoNCmRmX2NoYXJ0X3Rvam9pbiA8LSBkZl9jaGFydHNbLGMoIlRyYWNrX05hbWUiLCAiQXJ0aXN0IiwgIlVSTCIpXQ0KZGZfY2hhcnRfdG9qb2luJGlzaW5jaGFydCA8LSAxDQpkZl9hdWRpb19mZWF0dXJlc190b2pvaW4gPC0gZGZfYXVkaW9fZmVhdHVyZXNbLCBjKCJ0cmFja19uYW1lIiwiYXJ0aXN0X2tleSIsImV4dGVybmFsX3VybHNfc3BvdGlmeSIsImluc3RydW1lbnRhbG5lc3MiLCAiaW5zdHJ1bWVudGFsbmVzc19sb2dhZGp1c3QiKV0NCg0Kam9pbl9oaXN0b2dyYW0gPC0gZGZfYXVkaW9fZmVhdHVyZXNfdG9qb2luICU+JSANCiAgZHBseXI6OnNlbGVjdCgidHJhY2tfbmFtZSIsImFydGlzdF9rZXkiLCJleHRlcm5hbF91cmxzX3Nwb3RpZnkiLCJpbnN0cnVtZW50YWxuZXNzIiwgImluc3RydW1lbnRhbG5lc3NfbG9nYWRqdXN0IikgJT4lIA0KICBsZWZ0X2pvaW4oIGRmX2NoYXJ0X3Rvam9pbiAlPiUNCiAgICAgICAgICAgICAgIHNlbGVjdCgiVHJhY2tfTmFtZSIsICJBcnRpc3QiLCAiVVJMIiwiaXNpbmNoYXJ0IiksDQogICAgICAgICAgICAgICBieSA9IGMoDQogICAgICAgICAgICAgICAgICJ0cmFja19uYW1lIiA9ICJUcmFja19OYW1lIiwgDQogICAgICAgICAgICAgICAgICAgICAgImFydGlzdF9rZXkiID0iQXJ0aXN0IiwgDQogICAgICAgICAgICAgICAgICAgICAgImV4dGVybmFsX3VybHNfc3BvdGlmeSIgPSAiVVJMIikpDQoNCg0Kam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0W2lzLm5hKGpvaW5faGlzdG9ncmFtJGlzaW5jaGFydCldIDwtIDANCg0Kam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0IDwtIGZhY3Rvcihqb2luX2hpc3RvZ3JhbSRpc2luY2hhcnQpDQoNCg0KaDExIDwtIGhpc3Qoam9pbl9oaXN0b2dyYW1bam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0PT0xLCdpbnN0cnVtZW50YWxuZXNzJ10pDQpoMTEkZGVuc2l0eSA8LSAgaDExJGNvdW50cy9zdW0oaDExJGNvdW50cykqMTAwDQoNCmgxMiA8LSBoaXN0KGpvaW5faGlzdG9ncmFtW2pvaW5faGlzdG9ncmFtJGlzaW5jaGFydD09MCwnaW5zdHJ1bWVudGFsbmVzcyddKQ0KaDEyJGRlbnNpdHkgPC0gIGgxMiRjb3VudHMvc3VtKGgxMiRjb3VudHMpKjEwMA0KDQpoMjEgPC0gaGlzdChqb2luX2hpc3RvZ3JhbVtqb2luX2hpc3RvZ3JhbSRpc2luY2hhcnQ9PTEsJ2luc3RydW1lbnRhbG5lc3NfbG9nYWRqdXN0J10pDQpoMjEkZGVuc2l0eSA8LSAgaDIxJGNvdW50cy9zdW0oaDIxJGNvdW50cykqMTAwDQoNCmgyMiA8LSBoaXN0KGpvaW5faGlzdG9ncmFtW2pvaW5faGlzdG9ncmFtJGlzaW5jaGFydD09MCwnaW5zdHJ1bWVudGFsbmVzc19sb2dhZGp1c3QnXSkNCmgyMiRkZW5zaXR5IDwtICBoMjIkY291bnRzL3N1bShoMjIkY291bnRzKSoxMDANCg0KI3BuZygiQzovVXNlcnMvQXN1cy9EZXNrdG9wL0RBVEEgU0NJRU5DRS9NQUVTVFJJQS9EYXRhIE1pbmluZy9UUC9ncmFmaWNvcy9pbnN0cnVtZW50YWxuZXNzLnBuZyIsDQojICAgIHdpZHRoID0gODAwLCBoZWlnaHQgPSA4MDApDQpwYXIobWZyb3cgPSBjKDMsMikpDQpwbG90KGgxMSwgbWFpbj0naW5zdHJ1bWVudGFsbmVzcyBcbmNoYXJ0JywgeGxhYj0iIiwgeWxhYj0iUG9yY2VudGFnZSIsIGZyZXE9RkFMU0UsIGNvbD0nZ3JleScsIHlsaW0gPSBjKDAsMTAwKSkNCnBsb3QoaDEyLCBtYWluPSdpbnN0cnVtZW50YWxuZXNzIFxuZnVlcmEgY2hhcnQnLCB4bGFiPSIiLCB5bGFiPSJQb3JjZW50YWdlIiwgZnJlcT1GQUxTRSwgY29sPSdncmV5JywgeWxpbSA9IGMoMCwxMDApKQ0KcGxvdChoMjEsIG1haW4gPSJpbnN0cnVtZW50YWxuZXNzX2xvZyBcbmNoYXJ0IiwgeGxhYj0iIiwgeWxhYj0iUG9yY2VudGFnZSIsIGZyZXE9RkFMU0UsIGNvbD0nZ3JleScsIHlsaW0gPSBjKDAsMTAwKSkNCnBsb3QoaDIyLCBtYWluID0iaW5zdHJ1bWVudGFsbmVzc19sb2cgXG5mdWVyYSBjaGFydCIsIHhsYWI9IiIsIHlsYWI9IlBvcmNlbnRhZ2UiLCBmcmVxPUZBTFNFLCBjb2w9J2dyZXknLCB5bGltID0gYygwLDEwMCkpDQpib3hwbG90KGpvaW5faGlzdG9ncmFtW2pvaW5faGlzdG9ncmFtJGlzaW5jaGFydD09MSwnaW5zdHJ1bWVudGFsbmVzc19sb2dhZGp1c3QnXSwgbWFpbj0iaW5zdHJ1bWVudGFsbmVzc19sb2cgY2hhcnQiLCBob3Jpem9udGFsID0gVCkNCmJveHBsb3Qoam9pbl9oaXN0b2dyYW1bam9pbl9oaXN0b2dyYW0kaXNpbmNoYXJ0PT0wLCdpbnN0cnVtZW50YWxuZXNzX2xvZ2FkanVzdCddLCBtYWluPSJpbnN0cnVtZW50YWxuZXNzX2xvZyBmdWVyYSBjaGFydCIsIGhvcml6b250YWwgPSBUKQ0KI2Rldi5vZmYoKQ0KDQpgYGANCg0KDQoNCiMjIyBaLVNjb3JlIGRlIFZhcmlhYmxlcyBxdWUgInRpZW5kZW4gYSBsYSBub3JtYWwiDQpgYGB7cn0NCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyMgRklMVFJBTU9TIE9VVExJRVJTIFBPUiBaLVNDT1JFIHBhcmEgJ2RhbmNlYWJpbGl0eScsICd0ZW1wbycsICd2YWxlbmNlJw0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KI3otc2NvcmUgcGFyYSB2YXJpYWJsZXMgcXVlIHRpZW5kZW4gYSBsYSBub3JtYWwNCiNmaWx0cm8gZmVhdHVyZXMgbnVtZXJpY29zIA0KDQojZGl2aWRvIGxvcyBmZWF0dXJlcyBwb3Igc3UgZGlzdHJpYnVjacOzbg0KZmVhdHVyZXNfY29udGludWFzX21lZGlhIDwtIGMoJ2RhbmNlYWJpbGl0eScsICd0ZW1wbycsICd2YWxlbmNlJykNCmRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSA8LSBkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZXNfY29udGludWFzX21lZGlhXQ0KDQojbm9ybWFsaXpvIHogc2NvcmUgY29uIGxhcyB2YXJpYWJsZXMgcXVlIHRpZW5kZW4gYSBsYSBub3JtYWwNCg0KenNjb3JlX2NvbHMgPC0gYygpDQpmb3IoY29sIGluIG5hbWVzKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSkpew0KICBuYW1lX2NvbCA8LSBwYXN0ZSgnenNjb3JlXycsY29sLCBzZXAgPSAiIikNCiAgenNjb3JlX2NvbHMgPC0gYXBwZW5kKHpzY29yZV9jb2xzLCBuYW1lX2NvbCkNCiAgbWVkaWEgPC0gIG1lYW4oZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhWyxjb2xdKQ0KICBzdGR2IDwtIHNkKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYVssY29sXSkNCiAgZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhWyxuYW1lX2NvbF0gPC0gKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYVssY29sXSAtIG1lZGlhKS9zdGR2DQogIH0NCg0KcGFyKG1mcm93PWMoMSxsZW5ndGgoenNjb3JlX2NvbHMpKSkNCmxhcHBseSh6c2NvcmVfY29scywgZnVuY3Rpb24oY29sKSBib3hwbG90KGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYVssY29sXSx4bGFiPWNvbCkpDQpgYGANCg0KIyMjIEFuYWxpc2lzIGRlIFotU2NvcmUgcG9yIHZhcmlhYmxlDQogDQojIyMjIERhbmNlYWJpbGl0eQ0KDQpgYGB7cn0NCiN2YXJpYWJsZTogZGFuY2VhYmlsaXR5DQoNCnVtYnJhbF96c2NvcmUgPC0gMw0KY29uZGl0aW9ucyA8LSAoZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhJHpzY29yZV9kYW5jZWFiaWxpdHk+IHVtYnJhbF96c2NvcmUpIHwgKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSR6c2NvcmVfZGFuY2VhYmlsaXR5PCAtMSp1bWJyYWxfenNjb3JlKQ0KZGZfYXVkaW9fZmVhdHVyZXNbY29uZGl0aW9ucyxdICU+JQ0KICBzZWxlY3QoYWxidW1fbmFtZSxhcnRpc3RfbmFtZSwgZGFuY2VhYmlsaXR5ICkgJT4lDQogIGFycmFuZ2UoLWRhbmNlYWJpbGl0eSkNCmBgYA0KDQojIyMjIFRlbXBvDQoNCmBgYHtyfQ0KI3ZhcmlhYmxlOiBUZW1wbw0KDQp1bWJyYWxfenNjb3JlIDwtIDMNCmNvbmRpdGlvbnMgPC0gKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSR6c2NvcmVfdGVtcG8+IHVtYnJhbF96c2NvcmUpIHwgKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYSR6c2NvcmVfdGVtcG88IC0xKnVtYnJhbF96c2NvcmUpDQpkZl9hdWRpb19mZWF0dXJlc1tjb25kaXRpb25zLF0gJT4lDQogIHNlbGVjdChhbGJ1bV9uYW1lLGFydGlzdF9uYW1lLCB0ZW1wbyApICU+JQ0KICBhcnJhbmdlKC10ZW1wbykNCmBgYA0KDQojIyMjIFZhbGVuY2UNCg0KYGBge3J9DQojdmFyaWFibGU6IHZhbGVuY2UNCnVtYnJhbF96c2NvcmUgPC0gMw0KY29uZGl0aW9ucyA8LSAoZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhJHpzY29yZV92YWxlbmNlPiB1bWJyYWxfenNjb3JlKSB8IChkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWEkenNjb3JlX3ZhbGVuY2U8IC0xKnVtYnJhbF96c2NvcmUpDQpkZl9hdWRpb19mZWF0dXJlc1tjb25kaXRpb25zLF0gJT4lDQogIHNlbGVjdChhbGJ1bV9uYW1lLGFydGlzdF9uYW1lLCB2YWxlbmNlICkgJT4lDQogIGFycmFuZ2UoLXZhbGVuY2UpDQpgYGANCg0KIyMjIFotU2NvcmUgTW9kaWZpY2FkbyBkZSBWYXJpYWJsZXMgQXNpbWV0cmljYXMNCg0KYGBge3J9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyBGSUxUUkFNT1MgT1VUTElFUlMgUE9SIFotU0NPUkUgTU9ESUZJQ0FETyBwYXJhICdhY291c3RpY25lc3MnLCAnZHVyYXRpb25fbXMnLCAnZW5lcmd5JywgICdpbnN0cnVtZW50YWxuZXNzJywgJ2xpdmVuZXNzJywgJ2xvdWRuZXNzJywgJ3NwZWVjaGluZXNzJywgJ2NhbnRfbWFya2V0cycNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCmZlYXR1cmVzX2NvbnRpbnVhc19tZWRpYW5hIDwtIGMoJ2Fjb3VzdGljbmVzcycsICdkdXJhdGlvbl9tcycsICdlbmVyZ3knLCAnaW5zdHJ1bWVudGFsbmVzcycsICdsaXZlbmVzcycsICdsb3VkbmVzcycsICdzcGVlY2hpbmVzcycsICdjYW50X21hcmtldHMnKQ0KDQpkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYSA8LSBkZl9hdWRpb19mZWF0dXJlc1ssZmVhdHVyZXNfY29udGludWFzX21lZGlhbmFdDQoNCg0KDQp6c2NvcmVtb2RpZl9jb2xzIDwtIGMoKQ0KZm9yKGNvbCBpbiBuYW1lcyhkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYSkpew0KICBuYW1lX2NvbCA8LSBwYXN0ZSgnenNjb3JlbW9kaWZfJyxjb2wsIHNlcCA9ICIiKQ0KICB6c2NvcmVtb2RpZl9jb2xzIDwtIGFwcGVuZCh6c2NvcmVtb2RpZl9jb2xzLCBuYW1lX2NvbCkNCiAgbWVkID0gbWVkaWFuKGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYW5hWyxjb2xdLCBuYS5ybSA9IFQpDQogIE1BRCA9IG1lZGlhbihhYnMoZGZfYXVkaW9fZmVhdHVyZXNfenNjb3JlX21lZGlhbmFbLGNvbF0gLSBtZWQpLCBuYS5ybSA9IFQpDQogIGRmX2F1ZGlvX2ZlYXR1cmVzX3pzY29yZV9tZWRpYW5hWywgbmFtZV9jb2xdIDwtIDAuNjc0NSAqIChkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYVssY29sXSAtIG1lZCkgLyBNQUQNCn0NCg0KDQpwYXIobWZyb3c9Yyg0LDIpKQ0KbGFwcGx5KHpzY29yZW1vZGlmX2NvbHMsIGZ1bmN0aW9uKGNvbCkgYm94cGxvdChkZl9hdWRpb19mZWF0dXJlc196c2NvcmVfbWVkaWFuYVssY29sXSx4bGFiPWNvbCwgaG9yaXpvbnRhbCA9IFQpKQ0KDQpgYGANCg0KDQojIyMjIFJldmlzacOzbiBWYXJpYWJsZSBgSW5zdHJ1bWVudGFsbmVzc2ANCmBgYHtyfQ0KaW5zdHJ1bWVudGFsbmVzcyA8LSBjKCJpbnN0cnVtZW50YWxuZXNzIiwgInpzY29yZW1vZGlmX2luc3RydW1lbnRhbG5lc3MiKSANCg0KeCA8LSBkZl9hdWRpb19mZWF0dXJlcyRpbnN0cnVtZW50YWxuZXNzDQoNCm5faW50ZXJ2IDwtIDEwDQoNCg0KaW50ZXJ2YWxvcyA8LSByb3VuZChzZXEoMCxtYXgoeCksYnk9KG1heCh4KS1taW4oeCkpL25faW50ZXJ2KSwyKQ0KDQpsYWJzIDwtIGMoKQ0KZm9yIChpIGluIDE6bl9pbnRlcnYpew0KbGFiIDwtIHBhc3RlKGludGVydmFsb3NbaV0saW50ZXJ2YWxvc1tpKzFdLCBzZXA9J1xuJykNCmxhYnMgPC0gYXBwZW5kKGxhYnMsIGxhYikNCiAgICANCn0NCg0KYmlucyA8LSBjdXQoeCwgbl9pbnRlcnYsIGluY2x1ZGUubG93ZXN0ID0gVFJVRSwgbGFiZWxzID0gbGFicykNCg0KYmFycGxvdCh0YWJsZShiaW5zKSkNCg0KYGBgDQoNCkhhY2Vtb3MgSy1tZWFucyBwYXJhIHBvZGVyIGRpc2NyZXRpemFyIGxhIHZhcmlhYmxlLiANCg0KYGBge3J9DQpzc2UgPC0gYygpDQpmb3IgKGsgaW4gMjo2KXsNCiAgY2x1c3RlcnMgPC0ga21lYW5zKGRmX2F1ZGlvX2ZlYXR1cmVzJGluc3RydW1lbnRhbG5lc3MsY2VudGVycyA9IGssIGl0ZXIubWF4ID0gMTAsIG5zdGFydCA9IGspDQogIHNzZSA8LSBhcHBlbmQoc3NlLCBjbHVzdGVycyR0b3Qud2l0aGluc3MpDQp9DQoNCnBsb3QoMjo2LHNzZSwgdHlwZSA9ICdsJywgeGxhYj0nQ2FudGlkYWQgZGUgQ2x1c3RlcnMnLCB5bGFiPSdTdW1hIEVycm9yIEN1YWRyw6F0aWNvJykNCg0KI2s9MyANCmNsdXN0ZXJzMyA8LSBrbWVhbnMoZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzcyxjZW50ZXJzID0gMywgaXRlci5tYXggPSAxMCwgbnN0YXJ0ID0gMykNCg0KZGZfYXVkaW9fZmVhdHVyZXMkY2x1c3RlcnMgPC0gZmFjdG9yKGNsdXN0ZXJzMyRjbHVzdGVyKQ0KDQpsZXYgPC0gbGV2ZWxzKGRmX2F1ZGlvX2ZlYXR1cmVzJGNsdXN0ZXJzKQ0KDQpsYWJzIDwtIGMoKQ0KZm9yIChpIGluIGxldil7DQogIG1pbiA8LSBtaW4oZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzc1tkZl9hdWRpb19mZWF0dXJlcyRjbHVzdGVycz09aV0pDQogIG1heCA8LSBtYXgoZGZfYXVkaW9fZmVhdHVyZXMkaW5zdHJ1bWVudGFsbmVzc1tkZl9hdWRpb19mZWF0dXJlcyRjbHVzdGVycz09aV0pDQogIGxhYiA8LSBwYXN0ZShtaW4sbWF4LCBzZXA9JyAtICcpDQogIGxhYnMgPC0gYXBwZW5kKGxhYnMsIGxhYikNCn0NCg0KbGFicw0KDQojIGJhcnBsb3QodGFibGUoZmFjdG9yKGNsdXN0ZXJzMyRjbHVzdGVyKSksIGxhYmVscyA9IGxhYnMpDQoNCg0KDQpgYGANCg0KDQojIFByZWd1bnRhcyBkZSBpbnZlc3RpZ2FjaW9uDQoNCiMjIFBhdHJvbiBDb211biBDYW5jaW9uZXMgZGVsIENoYXJ0DQrCv1F1w6kgY2FyYWN0ZXLDrXN0aWNhcyB0aWVuZW4gbGFzIGNhbmNpb25lcyBxdWUgZXN0w6FuIGVuIGVsIGNoYXJ0PyDCv0N1YWwgZXMgZWwgcGF0csOzbiBjb211biBxdWUgdGllbmVuIGxhcyBjYW5jaW9uZXMgbcOhcyBlc2N1Y2hhZGFzPyAodmVyIGRpc3BlcnNpb25lcywgbWVkaWEsIGdyYWZpY28gY29tcGFyYXRpdm8pDQpgYGB7cn0NCg0KDQojZnVuY2lvbiBwYXJhIGVzY2FsYXIgdmFyaWFibGUNCnNjYWxlX3ZibGUgPC0gZnVuY3Rpb24oeCl7DQogICh4IC0gbWVhbih4LCBuYS5ybSA9IFQpKS9zZCh4LCBuYS5ybSA9IFQpDQp9DQoNCmBgYA0KYGBge3J9DQojYW50aV9qb2luDQphbnRpX2pvaW5fYXVkaW9fY2hhcnRzIDwtIGRmX2F1ZGlvX2ZlYXR1cmVzICU+JSANCiAgc2VsZWN0KCJhcnRpc3RfbmFtZSIsImFydGlzdF9hbGwiLCAiYXJ0aXN0X2tleSIsDQogICAgICAgICAidHJhY2tfbmFtZSIsICJleHRlcm5hbF91cmxzX3Nwb3RpZnkiLCAiYWxidW1fbmFtZSIsICJhbGJ1bV9yZWxlYXNlX3llYXIiLA0KICAgICAgICAgYWxsX29mKGZlYXR1cmVzX2NvbnRpbnVhcyksIGFsbF9vZihmZWF0dXJlc19jYXRlZ29yaWNhcykpICU+JSANCiAgYW50aV9qb2luKCBkZl9jaGFydHMgJT4lDQogICAgICAgICAgICAgICBzZWxlY3QoICJUcmFja19OYW1lIiwgIkFydGlzdCIsICJVUkwiKSwNCiAgICAgICAgICAgICAgIGJ5ID0gYygiZXh0ZXJuYWxfdXJsc19zcG90aWZ5IiA9IlVSTCIsDQogICAgICAgICAgICAgICAgICAgICAgImFydGlzdF9rZXkiID0iQXJ0aXN0IiAgKSkNCiAgICAgICAgICAgICAgICMgYnkgPSBjKCJ0cmFja19uYW1lIiA9ICJUcmFja19OYW1lIikpDQoNCg0KYW50aV9qb2luX2F1ZGlvX2NoYXJ0c19jb21wbGV0ZSA8LSBuYS5vbWl0KGFudGlfam9pbl9hdWRpb19jaGFydHMpDQphbnRpX2pvaW5fYXVkaW9fY2hhcnRzX2NvbXBsZXRlX3NjYWxlIDwtIGFudGlfam9pbl9hdWRpb19jaGFydHNfY29tcGxldGUgJT4lIA0KICBkaXN0aW5jdCgpICU+JSANCiAgc2VsZWN0KGZlYXR1cmVzX2NvbnRpbnVhcykgICU+JSANCiAgbXV0YXRlX2FsbChzY2FsZV92YmxlKQ0KbnJvdyhhbnRpX2pvaW5fYXVkaW9fY2hhcnRzX2NvbXBsZXRlX3NjYWxlKQ0KDQpgYGANCg0KIyMgUXXDqSB0ZW1hcyBwZXJkdXJhbiBtdWNobyBlbiBlbCByYW5raW5nDQoNCiMjIyBBcnRpc3RhcyBxdWUgbWFzIGFwYXJlY2VuIGVuIGVsIGNoYXJ0DQpgYGB7cn0NCmpvaW5fYXVkaW9fY2hhcnRzICU+JSANCiAgZ3JvdXBfYnkoYXJ0aXN0X25hbWUpICU+JSANCiAgZHBseXI6OnN1bW1hcmlzZShuID0gbigpKSAlPiUgDQogIGFycmFuZ2UoLW4pDQpgYGANCg0KIyMjIFRyYWNrcyBxdWUgbWFzIGFwYXJlY2VuIGVuIGVsIGNoYXJ0DQpgYGB7cn0NCmpvaW5fYXVkaW9fY2hhcnRzICU+JSANCiAgZ3JvdXBfYnkodHJhY2tfbmFtZSwgYXJ0aXN0X25hbWUsZXh0ZXJuYWxfdXJsc19zcG90aWZ5KSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpc2UobiA9IG4oKSkgJT4lIA0KICBhcnJhbmdlKC1uKSAlPiUgDQogIHNlbGVjdCh0cmFja19uYW1lLCBuLCBldmVyeXRoaW5nKC4pKQ0KDQpgYGANCg0KDQojIMK/Q3XDoW50byB0aWVtcG8gZXN0w6FuIGVuIHVuIGNoYXJ0PyANCg0KYGBge3J9DQojIGNhbnRpZGFkIGRlIHNlbWFuYXMgcXVlIGVzdHV2aWVyb24gZW4gZWwgY2hhcnQNCg0KZGZfY2hhcnRzICU+JSANCiAgbXV0YXRlKHdlZWtfc3RhcnQ9YXMuRGF0ZSh3ZWVrX3N0YXJ0KSwNCiAgICAgICAgIHdlZWtfZW5kID0gYXMuRGF0ZSh3ZWVrX2VuZCksDQogICAgICAgICB3ZWVrX3llYXIgPSAoeWVhcih3ZWVrX3N0YXJ0KSkpICU+JQ0KICBhcnJhbmdlKEFydGlzdCwgVHJhY2tfTmFtZSkgJT4lIA0KICBncm91cF9ieShBcnRpc3QsIFRyYWNrX05hbWUsIFVSTCkgJT4lIA0KIGRwbHlyOjogc3VtbWFyaXNlKCBkYXlfaW4gPSBtaW4od2Vla19zdGFydCksDQogICAgICAgICAgICAgeWVhcl9pbiA9IHllYXIoZGF5X2luKSwNCiAgICAgICAgICAgICBkYXlfbWF4ID0gbWF4KHdlZWtfZW5kKSwNCiAgICAgICAgICAgICB5ZWFyX21heCA9IHllYXIoZGF5X21heCksDQogICAgICAgICAgICAgZHVyYWNpb25fY2hhcnRfZGlhcyA9IGRheV9tYXgtZGF5X2luLA0KICAgICAgICAgICAgIGR1cmFjaW9uX2NoYXJ0X2FuaW8gPSB5ZWFyX21heCAtIHllYXJfaW4pICU+JSANCiAgYXJyYW5nZShBcnRpc3QpDQoNCmBgYA0KDQojcHJ1ZWJhIGlnYWwgZGUgdHJhbnNmb3JtYWNpb24geSB0ZXN0IGRlIG5vcm1hbGlkYWQNCmBgYHtyfQ0Kam9pbl9hdWRpb19jaGFydHNbMTo1LCJhY291c3RpY25lc3MiXV4yDQoNCmxpYnJhcnkobm9ydGVzdCkNCg0KbG9nMTAoZGZfY2hhcnRfd19seXJpY3MkYWNvdXN0aWNuZXNzKQ0KDQpmb3IgKGkgaW4gZmVhdHVyZXNfY29udGludWFzKXsNCiAgIHggPC0gbG9nMTAoZGZfY2hhcnRfd19seXJpY3NbLGldKQ0KICAgeCA8LSBzaGFwaXJvLnRlc3QoeCkNCiAgIHogPC0geCRwLnZhbHVlDQogIHByaW50KHopDQogIH0NCg0KDQpgYGANCg0KDQoNCg0K